@zoyth/simple-site-framework 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +572 -0
- package/bin/create-simple-site.js +390 -0
- package/bin/simple-site.js +664 -0
- package/dist/client.js +135 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +107 -0
- package/dist/client.mjs.map +1 -0
- package/dist/components/index.d.mts +3936 -0
- package/dist/components/index.d.ts +3936 -0
- package/dist/components/index.js +38265 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +38173 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/config/index.d.mts +298 -0
- package/dist/config/index.d.ts +298 -0
- package/dist/config/index.js +19 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +1 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/index.d.mts +2184 -0
- package/dist/index.d.ts +2184 -0
- package/dist/index.js +1713 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1605 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/i18n/index.js +665 -0
- package/dist/lib/i18n/index.js.map +1 -0
- package/dist/lib/i18n/index.mjs +621 -0
- package/dist/lib/i18n/index.mjs.map +1 -0
- package/docs/DOCUMENTATION-STRUCTURE.md +1156 -0
- package/docs/EXPORTS.md +125 -0
- package/docs/PERFORMANCE.md +757 -0
- package/docs/POLICY-PAGES.md +867 -0
- package/docs/ROADMAP.md +334 -0
- package/docs/SEO.md +455 -0
- package/docs/SITEMAP.md +708 -0
- package/docs/STRUCTURED-DATA.md +671 -0
- package/docs/accessibility/common-patterns.md +529 -0
- package/docs/accessibility/keyboard-navigation.md +263 -0
- package/docs/accessibility/overview.md +122 -0
- package/docs/accessibility/screen-readers.md +311 -0
- package/docs/accessibility/wcag-compliance.md +159 -0
- package/docs/api/README.md +164 -0
- package/docs/api/components/Accessibility.md +356 -0
- package/docs/api/components/Button.md +240 -0
- package/docs/api/components/HeroSection.md +306 -0
- package/docs/architecture/decisions.md +449 -0
- package/docs/components/AnalyticsTracker.md +58 -0
- package/docs/components/AnimatedCounter.md +48 -0
- package/docs/components/AnimatedSection.md +56 -0
- package/docs/components/BlogCard.md +42 -0
- package/docs/components/Checkbox.md +56 -0
- package/docs/components/CodeBlock.md +52 -0
- package/docs/components/ComparisonTable.md +40 -0
- package/docs/components/ComponentDemo.md +38 -0
- package/docs/components/CountdownTimer.md +51 -0
- package/docs/components/ExitIntentModal.md +56 -0
- package/docs/components/FAQAccordion.md +66 -0
- package/docs/components/FeaturesGrid.md +55 -0
- package/docs/components/FileUpload.md +54 -0
- package/docs/components/I18nMetaTags.md +55 -0
- package/docs/components/Icon.md +53 -0
- package/docs/components/LazySection.md +46 -0
- package/docs/components/LiveProof.md +53 -0
- package/docs/components/LoadingSpinner.md +46 -0
- package/docs/components/MultiStepForm.md +48 -0
- package/docs/components/PolicyLayout.md +55 -0
- package/docs/components/PricingTable.md +49 -0
- package/docs/components/Radio.md +59 -0
- package/docs/components/SEOMetaTags.md +58 -0
- package/docs/components/ScriptInjector.md +50 -0
- package/docs/components/Select.md +72 -0
- package/docs/components/Skeleton.md +47 -0
- package/docs/components/StatsSection.md +48 -0
- package/docs/components/StickyBar.md +62 -0
- package/docs/components/StructuredData.md +99 -0
- package/docs/components/StyleGuide.md +46 -0
- package/docs/components/TableOfContents.md +47 -0
- package/docs/components/TestimonialCarousel.md +42 -0
- package/docs/components/Timeline.md +51 -0
- package/docs/components/Toast.md +59 -0
- package/docs/components/TrackedLink.md +62 -0
- package/docs/components/TrustBadges.md +44 -0
- package/docs/components/conversion/MobileCTA.md +363 -0
- package/docs/components/forms/ContactForm.md +75 -0
- package/docs/components/forms/FormField.md +74 -0
- package/docs/components/layout/Footer.md +601 -0
- package/docs/components/layout/Header.md +549 -0
- package/docs/components/layout/LanguageSelector.md +54 -0
- package/docs/components/layout/LanguageSwitcher.md +24 -0
- package/docs/components/overview.md +447 -0
- package/docs/components/sections/AboutSection.md +48 -0
- package/docs/components/sections/CTASection.md +596 -0
- package/docs/components/sections/CaseStudySection.md +47 -0
- package/docs/components/sections/ContactSection.md +599 -0
- package/docs/components/sections/FeatureSection.md +44 -0
- package/docs/components/sections/HeroSection.md +404 -0
- package/docs/components/sections/LogosSection.md +47 -0
- package/docs/components/sections/PersonalTaxesSection.md +23 -0
- package/docs/components/sections/RecruitingSection.md +23 -0
- package/docs/components/sections/SecurePortalSection.md +23 -0
- package/docs/components/sections/ServicePageLayout.md +52 -0
- package/docs/components/sections/ServicesSection.md +49 -0
- package/docs/components/sections/TestimonialSection.md +44 -0
- package/docs/components/sections/WhyChooseUsSection.md +54 -0
- package/docs/components/ui/Breadcrumb.md +70 -0
- package/docs/components/ui/Button.md +514 -0
- package/docs/components/ui/Card.md +501 -0
- package/docs/components/ui/Input.md +54 -0
- package/docs/components/ui/MobileLinks.md +43 -0
- package/docs/components/ui/Modal.md +60 -0
- package/docs/components/ui/Tabs.md +62 -0
- package/docs/components/ui/Textarea.md +52 -0
- package/docs/core-concepts/configuration-driven.md +552 -0
- package/docs/core-concepts/overview.md +351 -0
- package/docs/features/accessibility/README.md +73 -0
- package/docs/features/accessibility/aria-support.md +177 -0
- package/docs/features/accessibility/color-contrast.md +155 -0
- package/docs/features/accessibility/focus-management.md +187 -0
- package/docs/features/accessibility/testing.md +196 -0
- package/docs/features/analytics/README.md +51 -0
- package/docs/features/analytics/ab-testing.md +171 -0
- package/docs/features/analytics/conversion-tracking.md +207 -0
- package/docs/features/analytics/custom-events.md +219 -0
- package/docs/features/analytics/privacy.md +198 -0
- package/docs/features/analytics/setup.md +114 -0
- package/docs/features/analytics/tracking-events.md +224 -0
- package/docs/features/i18n/README.md +51 -0
- package/docs/features/i18n/best-practices.md +273 -0
- package/docs/features/i18n/configuration.md +84 -0
- package/docs/features/i18n/formatting.md +133 -0
- package/docs/features/i18n/locale-detection.md +122 -0
- package/docs/features/i18n/routing.md +99 -0
- package/docs/features/i18n/rtl-support.md +191 -0
- package/docs/features/i18n/translations.md +129 -0
- package/docs/features/internationalization.md +595 -0
- package/docs/features/performance/README.md +77 -0
- package/docs/features/performance/bundle-size.md +134 -0
- package/docs/features/performance/caching.md +131 -0
- package/docs/features/performance/code-splitting.md +121 -0
- package/docs/features/performance/image-optimization.md +110 -0
- package/docs/features/performance/lazy-loading.md +92 -0
- package/docs/features/performance/monitoring.md +148 -0
- package/docs/features/seo/README.md +51 -0
- package/docs/features/seo/best-practices.md +184 -0
- package/docs/features/seo/canonical-urls.md +182 -0
- package/docs/features/seo/meta-tags.md +126 -0
- package/docs/features/seo/open-graph.md +166 -0
- package/docs/features/seo/robots-txt.md +146 -0
- package/docs/features/seo/sitemaps.md +162 -0
- package/docs/features/seo/structured-data.md +166 -0
- package/docs/getting-started/installation.md +292 -0
- package/docs/getting-started/introduction.md +195 -0
- package/docs/getting-started/quick-start.md +460 -0
- package/docs/guides/analytics-setup.md +616 -0
- package/docs/i18n/CONFIGURATION.md +353 -0
- package/docs/i18n/EXAMPLES.md +402 -0
- package/docs/i18n/MIGRATION.md +260 -0
- package/docs/i18n/SEO.md +392 -0
- package/docs/i18n/STATIC-GENERATION-FIX.md +71 -0
- package/docs/migration/changelog.md +136 -0
- package/docs/migration/overview.md +233 -0
- package/docs/recipes/adding-animations.md +475 -0
- package/docs/recipes/forms-with-validation.md +393 -0
- package/package.json +152 -0
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
# Analytics Setup
|
|
2
|
+
|
|
3
|
+
Complete guide to setting up analytics tracking with Simple Site Framework.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The framework provides built-in analytics utilities for:
|
|
8
|
+
|
|
9
|
+
- **Page view tracking** - Automatic on route changes
|
|
10
|
+
- **Scroll depth tracking** - Engagement milestones (25%, 50%, 75%, 100%)
|
|
11
|
+
- **CTA click tracking** - Button and link conversions
|
|
12
|
+
- **A/B test events** - Test variant tracking
|
|
13
|
+
- **Custom events** - Flexible event tracking
|
|
14
|
+
|
|
15
|
+
All analytics push to `window.dataLayer` for Google Tag Manager compatibility.
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
### 1. Add Analytics Tracker
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// src/app/layout.tsx
|
|
23
|
+
import { AnalyticsTracker } from '@zoyth/simple-site-framework/client';
|
|
24
|
+
|
|
25
|
+
export default function RootLayout({ children }) {
|
|
26
|
+
return (
|
|
27
|
+
<html>
|
|
28
|
+
<body>
|
|
29
|
+
<AnalyticsTracker />
|
|
30
|
+
{children}
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This automatically tracks:
|
|
38
|
+
- Page views on route changes
|
|
39
|
+
- Scroll depth at 25%, 50%, 75%, 100%
|
|
40
|
+
|
|
41
|
+
### 2. Add Google Tag Manager
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// src/app/layout.tsx
|
|
45
|
+
import Script from 'next/script';
|
|
46
|
+
|
|
47
|
+
export default function RootLayout({ children }) {
|
|
48
|
+
return (
|
|
49
|
+
<html>
|
|
50
|
+
<head>
|
|
51
|
+
<Script id="gtm" strategy="afterInteractive">
|
|
52
|
+
{`
|
|
53
|
+
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
|
54
|
+
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
|
55
|
+
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
56
|
+
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
|
57
|
+
})(window,document,'script','dataLayer','GTM-XXXXXX');
|
|
58
|
+
`}
|
|
59
|
+
</Script>
|
|
60
|
+
</head>
|
|
61
|
+
<body>
|
|
62
|
+
<AnalyticsTracker />
|
|
63
|
+
{children}
|
|
64
|
+
</body>
|
|
65
|
+
</html>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Replace `GTM-XXXXXX` with your GTM container ID.
|
|
71
|
+
|
|
72
|
+
## Page View Tracking
|
|
73
|
+
|
|
74
|
+
### Automatic Tracking
|
|
75
|
+
|
|
76
|
+
`AnalyticsTracker` automatically tracks page views on route changes:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// Pushed to dataLayer:
|
|
80
|
+
{
|
|
81
|
+
event: 'page_view',
|
|
82
|
+
page_path: '/about',
|
|
83
|
+
page_title: 'About Us - Your Company'
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Manual Page View
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { trackPageView } from '@zoyth/simple-site-framework/lib/analytics';
|
|
91
|
+
|
|
92
|
+
trackPageView('/custom-page', 'Custom Page Title');
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### With Metadata
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
trackPageView('/product/123', 'Product Details', {
|
|
99
|
+
product_id: '123',
|
|
100
|
+
category: 'electronics',
|
|
101
|
+
price: 99.99
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Scroll Depth Tracking
|
|
106
|
+
|
|
107
|
+
### Automatic Tracking
|
|
108
|
+
|
|
109
|
+
`AnalyticsTracker` tracks scroll milestones:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// Pushed to dataLayer at each milestone:
|
|
113
|
+
{
|
|
114
|
+
event: 'scroll_depth',
|
|
115
|
+
event_category: 'engagement',
|
|
116
|
+
event_label: '25%',
|
|
117
|
+
scroll_percentage: 25,
|
|
118
|
+
page_path: '/about'
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Milestones: 25%, 50%, 75%, 100%
|
|
123
|
+
|
|
124
|
+
### Manual Scroll Tracking
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { trackScrollDepth } from '@zoyth/simple-site-framework/lib/analytics';
|
|
128
|
+
|
|
129
|
+
trackScrollDepth(50, '/about'); // Track 50% scroll
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Custom Milestones
|
|
133
|
+
|
|
134
|
+
Create your own scroll tracker:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
'use client';
|
|
138
|
+
|
|
139
|
+
import { useEffect, useRef } from 'react';
|
|
140
|
+
import { trackScrollDepth } from '@zoyth/simple-site-framework/lib/analytics';
|
|
141
|
+
|
|
142
|
+
export function CustomScrollTracker({ milestones = [10, 30, 60, 90] }) {
|
|
143
|
+
const tracked = useRef(new Set());
|
|
144
|
+
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
const handleScroll = () => {
|
|
147
|
+
const windowHeight = window.innerHeight;
|
|
148
|
+
const documentHeight = document.documentElement.scrollHeight;
|
|
149
|
+
const scrollTop = window.scrollY;
|
|
150
|
+
const scrollPercent = (scrollTop / (documentHeight - windowHeight)) * 100;
|
|
151
|
+
|
|
152
|
+
for (const milestone of milestones) {
|
|
153
|
+
if (scrollPercent >= milestone && !tracked.current.has(milestone)) {
|
|
154
|
+
tracked.current.add(milestone);
|
|
155
|
+
trackScrollDepth(milestone, window.location.pathname);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
161
|
+
return () => window.removeEventListener('scroll', handleScroll);
|
|
162
|
+
}, [milestones]);
|
|
163
|
+
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## CTA Click Tracking
|
|
169
|
+
|
|
170
|
+
### Opt-in Click Tracking
|
|
171
|
+
|
|
172
|
+
Components support optional `onClick` for tracking:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
<HeroSection
|
|
176
|
+
heading="Welcome"
|
|
177
|
+
cta={{
|
|
178
|
+
text: 'Get Started',
|
|
179
|
+
href: '/signup',
|
|
180
|
+
onClick: () => {
|
|
181
|
+
// Your tracking logic
|
|
182
|
+
trackCTAClick('hero_cta', { location: 'homepage' });
|
|
183
|
+
}
|
|
184
|
+
}}
|
|
185
|
+
/>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Track Link Clicks
|
|
189
|
+
|
|
190
|
+
Use `TrackedLink` for automatic link tracking:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { TrackedLink } from '@zoyth/simple-site-framework';
|
|
194
|
+
|
|
195
|
+
<TrackedLink
|
|
196
|
+
href="/pricing"
|
|
197
|
+
eventName="pricing_link_click"
|
|
198
|
+
eventData={{ location: 'footer' }}
|
|
199
|
+
>
|
|
200
|
+
View Pricing
|
|
201
|
+
</TrackedLink>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Pushed to dataLayer:
|
|
205
|
+
```typescript
|
|
206
|
+
{
|
|
207
|
+
event: 'link_click',
|
|
208
|
+
event_category: 'navigation',
|
|
209
|
+
event_label: 'pricing_link_click',
|
|
210
|
+
link_url: '/pricing',
|
|
211
|
+
...eventData
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Track Button Clicks
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import { trackEvent } from '@zoyth/simple-site-framework/lib/analytics';
|
|
219
|
+
|
|
220
|
+
<Button
|
|
221
|
+
onClick={() => {
|
|
222
|
+
trackEvent('button_click', {
|
|
223
|
+
button_text: 'Download',
|
|
224
|
+
button_location: 'hero',
|
|
225
|
+
file_type: 'pdf'
|
|
226
|
+
});
|
|
227
|
+
}}
|
|
228
|
+
>
|
|
229
|
+
Download PDF
|
|
230
|
+
</Button>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## A/B Test Tracking
|
|
234
|
+
|
|
235
|
+
### Track A/B Test Events
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { getABTestVariant, trackABTestEvent } from '@zoyth/simple-site-framework/client';
|
|
239
|
+
|
|
240
|
+
// Get variant
|
|
241
|
+
const variant = getABTestVariant({
|
|
242
|
+
testId: 'hero_cta_test',
|
|
243
|
+
variants: { A: { weight: 50 }, B: { weight: 50 } }
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Show variant
|
|
247
|
+
const ctaText = variant === 'A' ? 'Get Started' : 'Start Free Trial';
|
|
248
|
+
|
|
249
|
+
// Track conversion
|
|
250
|
+
<Button
|
|
251
|
+
onClick={() => {
|
|
252
|
+
trackABTestEvent('hero_cta_test', variant, 'conversion', {
|
|
253
|
+
revenue: 99.99
|
|
254
|
+
});
|
|
255
|
+
}}
|
|
256
|
+
>
|
|
257
|
+
{ctaText}
|
|
258
|
+
</Button>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Pushed to dataLayer:
|
|
262
|
+
```typescript
|
|
263
|
+
{
|
|
264
|
+
event: 'ab_test_event',
|
|
265
|
+
event_category: 'ab_test',
|
|
266
|
+
event_label: 'hero_cta_test_A_conversion',
|
|
267
|
+
test_id: 'hero_cta_test',
|
|
268
|
+
variant: 'A',
|
|
269
|
+
test_event: 'conversion',
|
|
270
|
+
revenue: 99.99
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Custom Events
|
|
275
|
+
|
|
276
|
+
### Basic Custom Event
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { trackEvent } from '@zoyth/simple-site-framework/lib/analytics';
|
|
280
|
+
|
|
281
|
+
trackEvent('video_play', {
|
|
282
|
+
video_title: 'Product Demo',
|
|
283
|
+
video_duration: 120,
|
|
284
|
+
video_percent: 0
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### E-commerce Events
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
// Add to cart
|
|
292
|
+
trackEvent('add_to_cart', {
|
|
293
|
+
item_id: 'SKU123',
|
|
294
|
+
item_name: 'Premium Plan',
|
|
295
|
+
price: 99.99,
|
|
296
|
+
currency: 'USD',
|
|
297
|
+
quantity: 1
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Begin checkout
|
|
301
|
+
trackEvent('begin_checkout', {
|
|
302
|
+
value: 99.99,
|
|
303
|
+
currency: 'USD',
|
|
304
|
+
items: [{
|
|
305
|
+
item_id: 'SKU123',
|
|
306
|
+
item_name: 'Premium Plan',
|
|
307
|
+
price: 99.99
|
|
308
|
+
}]
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Purchase
|
|
312
|
+
trackEvent('purchase', {
|
|
313
|
+
transaction_id: 'T12345',
|
|
314
|
+
value: 99.99,
|
|
315
|
+
currency: 'USD',
|
|
316
|
+
items: [...]
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Form Events
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// Form start
|
|
324
|
+
trackEvent('form_start', {
|
|
325
|
+
form_id: 'contact_form',
|
|
326
|
+
form_name: 'Contact Us'
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Form submit
|
|
330
|
+
trackEvent('form_submit', {
|
|
331
|
+
form_id: 'contact_form',
|
|
332
|
+
form_name: 'Contact Us',
|
|
333
|
+
submission_time: Date.now()
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Form error
|
|
337
|
+
trackEvent('form_error', {
|
|
338
|
+
form_id: 'contact_form',
|
|
339
|
+
error_field: 'email',
|
|
340
|
+
error_message: 'Invalid email format'
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Google Tag Manager Setup
|
|
345
|
+
|
|
346
|
+
### 1. Create GTM Container
|
|
347
|
+
|
|
348
|
+
1. Go to [tagmanager.google.com](https://tagmanager.google.com)
|
|
349
|
+
2. Create a new container for your website
|
|
350
|
+
3. Copy the GTM container ID (GTM-XXXXXX)
|
|
351
|
+
|
|
352
|
+
### 2. Add GTM Script
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// src/app/layout.tsx
|
|
356
|
+
<Script id="gtm" strategy="afterInteractive">
|
|
357
|
+
{`
|
|
358
|
+
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
|
359
|
+
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
|
360
|
+
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
361
|
+
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
|
362
|
+
})(window,document,'script','dataLayer','${process.env.NEXT_PUBLIC_GTM_ID}');
|
|
363
|
+
`}
|
|
364
|
+
</Script>
|
|
365
|
+
|
|
366
|
+
<!-- GTM noscript (in <body>) -->
|
|
367
|
+
<noscript>
|
|
368
|
+
<iframe
|
|
369
|
+
src={`https://www.googletagmanager.com/ns.html?id=${process.env.NEXT_PUBLIC_GTM_ID}`}
|
|
370
|
+
height="0"
|
|
371
|
+
width="0"
|
|
372
|
+
style={{ display: 'none', visibility: 'hidden' }}
|
|
373
|
+
/>
|
|
374
|
+
</noscript>
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### 3. Configure Tags in GTM
|
|
378
|
+
|
|
379
|
+
**Page View Tag:**
|
|
380
|
+
1. New Tag → Google Analytics: GA4 Event
|
|
381
|
+
2. Event Name: `page_view`
|
|
382
|
+
3. Trigger: Custom Event → `page_view`
|
|
383
|
+
4. Add Event Parameters from dataLayer variables
|
|
384
|
+
|
|
385
|
+
**Scroll Depth Tag:**
|
|
386
|
+
1. New Tag → Google Analytics: GA4 Event
|
|
387
|
+
2. Event Name: `scroll`
|
|
388
|
+
3. Trigger: Custom Event → `scroll_depth`
|
|
389
|
+
4. Add parameters: scroll_percentage, page_path
|
|
390
|
+
|
|
391
|
+
**Custom Event Tag:**
|
|
392
|
+
1. New Tag → Google Analytics: GA4 Event
|
|
393
|
+
2. Event Name: `{{Event}}` (use Event variable)
|
|
394
|
+
3. Trigger: All Custom Events
|
|
395
|
+
4. Pass all dataLayer variables as parameters
|
|
396
|
+
|
|
397
|
+
## Environment-Specific Tracking
|
|
398
|
+
|
|
399
|
+
### Development vs Production
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
// src/lib/analytics/config.ts
|
|
403
|
+
export const analyticsConfig = {
|
|
404
|
+
enabled: process.env.NODE_ENV === 'production',
|
|
405
|
+
gtmId: process.env.NEXT_PUBLIC_GTM_ID,
|
|
406
|
+
debug: process.env.NODE_ENV === 'development',
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// Conditional tracking
|
|
410
|
+
import { analyticsConfig } from '@/lib/analytics/config';
|
|
411
|
+
|
|
412
|
+
function trackEvent(eventName, data) {
|
|
413
|
+
if (!analyticsConfig.enabled) {
|
|
414
|
+
if (analyticsConfig.debug) {
|
|
415
|
+
console.log('[Analytics Debug]', eventName, data);
|
|
416
|
+
}
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Real tracking
|
|
421
|
+
pushToDataLayer({ event: eventName, ...data });
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Multiple Environments
|
|
426
|
+
|
|
427
|
+
```env
|
|
428
|
+
# .env.local
|
|
429
|
+
NEXT_PUBLIC_GTM_ID=GTM-DEV123
|
|
430
|
+
|
|
431
|
+
# .env.production
|
|
432
|
+
NEXT_PUBLIC_GTM_ID=GTM-PROD456
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
## Privacy & GDPR Compliance
|
|
436
|
+
|
|
437
|
+
### Cookie Consent Integration
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
'use client';
|
|
441
|
+
|
|
442
|
+
import { AnalyticsTracker } from '@zoyth/simple-site-framework/client';
|
|
443
|
+
import { useEffect, useState } from 'react';
|
|
444
|
+
|
|
445
|
+
export function ConditionalAnalytics() {
|
|
446
|
+
const [consent, setConsent] = useState(false);
|
|
447
|
+
|
|
448
|
+
useEffect(() => {
|
|
449
|
+
// Check cookie consent
|
|
450
|
+
const hasConsent = getCookieConsent();
|
|
451
|
+
setConsent(hasConsent);
|
|
452
|
+
}, []);
|
|
453
|
+
|
|
454
|
+
if (!consent) return null;
|
|
455
|
+
|
|
456
|
+
return <AnalyticsTracker />;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function getCookieConsent() {
|
|
460
|
+
// Check your consent management platform
|
|
461
|
+
return localStorage.getItem('cookie_consent') === 'granted';
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Anonymize IP Addresses
|
|
466
|
+
|
|
467
|
+
Configure in GTM:
|
|
468
|
+
1. Open your GA4 Configuration Tag
|
|
469
|
+
2. Add Field: `anonymize_ip` → `true`
|
|
470
|
+
|
|
471
|
+
### Disable Tracking
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
// Respect Do Not Track
|
|
475
|
+
const dnt = navigator.doNotTrack || window.doNotTrack;
|
|
476
|
+
if (dnt === '1' || dnt === 'yes') {
|
|
477
|
+
// Don't load AnalyticsTracker
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## Testing & Debugging
|
|
482
|
+
|
|
483
|
+
### Enable Debug Mode
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
// Development console logs
|
|
487
|
+
if (process.env.NODE_ENV === 'development') {
|
|
488
|
+
console.log('[Analytics]', {
|
|
489
|
+
event: 'page_view',
|
|
490
|
+
page_path: '/about'
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### GTM Preview Mode
|
|
496
|
+
|
|
497
|
+
1. Open GTM
|
|
498
|
+
2. Click "Preview"
|
|
499
|
+
3. Enter your site URL
|
|
500
|
+
4. See all events in real-time
|
|
501
|
+
|
|
502
|
+
### Chrome DevTools
|
|
503
|
+
|
|
504
|
+
```javascript
|
|
505
|
+
// View dataLayer in console
|
|
506
|
+
window.dataLayer
|
|
507
|
+
// See all pushed events
|
|
508
|
+
|
|
509
|
+
// Watch for new events
|
|
510
|
+
window.dataLayer.push = new Proxy(window.dataLayer.push, {
|
|
511
|
+
apply(target, thisArg, args) {
|
|
512
|
+
console.log('[DataLayer Push]', args[0]);
|
|
513
|
+
return target.apply(thisArg, args);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### GA4 DebugView
|
|
519
|
+
|
|
520
|
+
1. Open GA4 property
|
|
521
|
+
2. Go to DebugView (in Reports)
|
|
522
|
+
3. See real-time events from preview/debug mode
|
|
523
|
+
|
|
524
|
+
## Best Practices
|
|
525
|
+
|
|
526
|
+
### ✅ Do
|
|
527
|
+
|
|
528
|
+
- Track meaningful conversions (signups, purchases, form submits)
|
|
529
|
+
- Use consistent event naming (snake_case recommended)
|
|
530
|
+
- Include relevant context in event data
|
|
531
|
+
- Test tracking before deploying
|
|
532
|
+
- Document your tracking plan
|
|
533
|
+
- Respect user privacy and consent
|
|
534
|
+
- Use server-side tracking for sensitive data
|
|
535
|
+
|
|
536
|
+
### ❌ Don't
|
|
537
|
+
|
|
538
|
+
- Track personally identifiable information (PII)
|
|
539
|
+
- Send excessive events (rate limits)
|
|
540
|
+
- Track every click/interaction
|
|
541
|
+
- Use inconsistent event naming
|
|
542
|
+
- Forget to test in GTM preview
|
|
543
|
+
- Ignore GDPR/privacy regulations
|
|
544
|
+
- Track user input values directly
|
|
545
|
+
|
|
546
|
+
## Common Patterns
|
|
547
|
+
|
|
548
|
+
### Track Form Conversions
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
<ContactForm
|
|
552
|
+
fields={[...]}
|
|
553
|
+
onSubmit={async (data) => {
|
|
554
|
+
// Track form start
|
|
555
|
+
trackEvent('form_start', { form_id: 'contact' });
|
|
556
|
+
|
|
557
|
+
try {
|
|
558
|
+
await submitForm(data);
|
|
559
|
+
|
|
560
|
+
// Track successful submission
|
|
561
|
+
trackEvent('form_submit', {
|
|
562
|
+
form_id: 'contact',
|
|
563
|
+
form_name: 'Contact Form'
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// Track as conversion
|
|
567
|
+
trackEvent('generate_lead', {
|
|
568
|
+
lead_source: 'contact_form'
|
|
569
|
+
});
|
|
570
|
+
} catch (error) {
|
|
571
|
+
// Track error
|
|
572
|
+
trackEvent('form_error', {
|
|
573
|
+
form_id: 'contact',
|
|
574
|
+
error: error.message
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}}
|
|
578
|
+
/>
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Track Video Engagement
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
<video
|
|
585
|
+
onPlay={() => trackEvent('video_play', { video_title: 'Demo' })}
|
|
586
|
+
onPause={() => trackEvent('video_pause', { video_title: 'Demo' })}
|
|
587
|
+
onEnded={() => trackEvent('video_complete', { video_title: 'Demo' })}
|
|
588
|
+
/>
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### Track Search
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
function SearchBar() {
|
|
595
|
+
const [query, setQuery] = useState('');
|
|
596
|
+
|
|
597
|
+
const handleSearch = () => {
|
|
598
|
+
trackEvent('search', {
|
|
599
|
+
search_term: query,
|
|
600
|
+
search_location: 'header'
|
|
601
|
+
});
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
return <input onChange={e => setQuery(e.target.value)} onSubmit={handleSearch} />;
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
## Related Documentation
|
|
609
|
+
|
|
610
|
+
- **[AnalyticsTracker](../components/utilities/AnalyticsTracker.md)** - Component API
|
|
611
|
+
- **[TrackedLink](../components/utilities/TrackedLink.md)** - Link tracking
|
|
612
|
+
- **[A/B Testing](./ab-testing.md)** - Experimentation setup
|
|
613
|
+
|
|
614
|
+
## API Reference
|
|
615
|
+
|
|
616
|
+
Full analytics utilities: **[API Reference](../api/utilities.md#analytics)**
|