@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.
Files changed (166) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +572 -0
  3. package/bin/create-simple-site.js +390 -0
  4. package/bin/simple-site.js +664 -0
  5. package/dist/client.js +135 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/client.mjs +107 -0
  8. package/dist/client.mjs.map +1 -0
  9. package/dist/components/index.d.mts +3936 -0
  10. package/dist/components/index.d.ts +3936 -0
  11. package/dist/components/index.js +38265 -0
  12. package/dist/components/index.js.map +1 -0
  13. package/dist/components/index.mjs +38173 -0
  14. package/dist/components/index.mjs.map +1 -0
  15. package/dist/config/index.d.mts +298 -0
  16. package/dist/config/index.d.ts +298 -0
  17. package/dist/config/index.js +19 -0
  18. package/dist/config/index.js.map +1 -0
  19. package/dist/config/index.mjs +1 -0
  20. package/dist/config/index.mjs.map +1 -0
  21. package/dist/index.d.mts +2184 -0
  22. package/dist/index.d.ts +2184 -0
  23. package/dist/index.js +1713 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/index.mjs +1605 -0
  26. package/dist/index.mjs.map +1 -0
  27. package/dist/lib/i18n/index.js +665 -0
  28. package/dist/lib/i18n/index.js.map +1 -0
  29. package/dist/lib/i18n/index.mjs +621 -0
  30. package/dist/lib/i18n/index.mjs.map +1 -0
  31. package/docs/DOCUMENTATION-STRUCTURE.md +1156 -0
  32. package/docs/EXPORTS.md +125 -0
  33. package/docs/PERFORMANCE.md +757 -0
  34. package/docs/POLICY-PAGES.md +867 -0
  35. package/docs/ROADMAP.md +334 -0
  36. package/docs/SEO.md +455 -0
  37. package/docs/SITEMAP.md +708 -0
  38. package/docs/STRUCTURED-DATA.md +671 -0
  39. package/docs/accessibility/common-patterns.md +529 -0
  40. package/docs/accessibility/keyboard-navigation.md +263 -0
  41. package/docs/accessibility/overview.md +122 -0
  42. package/docs/accessibility/screen-readers.md +311 -0
  43. package/docs/accessibility/wcag-compliance.md +159 -0
  44. package/docs/api/README.md +164 -0
  45. package/docs/api/components/Accessibility.md +356 -0
  46. package/docs/api/components/Button.md +240 -0
  47. package/docs/api/components/HeroSection.md +306 -0
  48. package/docs/architecture/decisions.md +449 -0
  49. package/docs/components/AnalyticsTracker.md +58 -0
  50. package/docs/components/AnimatedCounter.md +48 -0
  51. package/docs/components/AnimatedSection.md +56 -0
  52. package/docs/components/BlogCard.md +42 -0
  53. package/docs/components/Checkbox.md +56 -0
  54. package/docs/components/CodeBlock.md +52 -0
  55. package/docs/components/ComparisonTable.md +40 -0
  56. package/docs/components/ComponentDemo.md +38 -0
  57. package/docs/components/CountdownTimer.md +51 -0
  58. package/docs/components/ExitIntentModal.md +56 -0
  59. package/docs/components/FAQAccordion.md +66 -0
  60. package/docs/components/FeaturesGrid.md +55 -0
  61. package/docs/components/FileUpload.md +54 -0
  62. package/docs/components/I18nMetaTags.md +55 -0
  63. package/docs/components/Icon.md +53 -0
  64. package/docs/components/LazySection.md +46 -0
  65. package/docs/components/LiveProof.md +53 -0
  66. package/docs/components/LoadingSpinner.md +46 -0
  67. package/docs/components/MultiStepForm.md +48 -0
  68. package/docs/components/PolicyLayout.md +55 -0
  69. package/docs/components/PricingTable.md +49 -0
  70. package/docs/components/Radio.md +59 -0
  71. package/docs/components/SEOMetaTags.md +58 -0
  72. package/docs/components/ScriptInjector.md +50 -0
  73. package/docs/components/Select.md +72 -0
  74. package/docs/components/Skeleton.md +47 -0
  75. package/docs/components/StatsSection.md +48 -0
  76. package/docs/components/StickyBar.md +62 -0
  77. package/docs/components/StructuredData.md +99 -0
  78. package/docs/components/StyleGuide.md +46 -0
  79. package/docs/components/TableOfContents.md +47 -0
  80. package/docs/components/TestimonialCarousel.md +42 -0
  81. package/docs/components/Timeline.md +51 -0
  82. package/docs/components/Toast.md +59 -0
  83. package/docs/components/TrackedLink.md +62 -0
  84. package/docs/components/TrustBadges.md +44 -0
  85. package/docs/components/conversion/MobileCTA.md +363 -0
  86. package/docs/components/forms/ContactForm.md +75 -0
  87. package/docs/components/forms/FormField.md +74 -0
  88. package/docs/components/layout/Footer.md +601 -0
  89. package/docs/components/layout/Header.md +549 -0
  90. package/docs/components/layout/LanguageSelector.md +54 -0
  91. package/docs/components/layout/LanguageSwitcher.md +24 -0
  92. package/docs/components/overview.md +447 -0
  93. package/docs/components/sections/AboutSection.md +48 -0
  94. package/docs/components/sections/CTASection.md +596 -0
  95. package/docs/components/sections/CaseStudySection.md +47 -0
  96. package/docs/components/sections/ContactSection.md +599 -0
  97. package/docs/components/sections/FeatureSection.md +44 -0
  98. package/docs/components/sections/HeroSection.md +404 -0
  99. package/docs/components/sections/LogosSection.md +47 -0
  100. package/docs/components/sections/PersonalTaxesSection.md +23 -0
  101. package/docs/components/sections/RecruitingSection.md +23 -0
  102. package/docs/components/sections/SecurePortalSection.md +23 -0
  103. package/docs/components/sections/ServicePageLayout.md +52 -0
  104. package/docs/components/sections/ServicesSection.md +49 -0
  105. package/docs/components/sections/TestimonialSection.md +44 -0
  106. package/docs/components/sections/WhyChooseUsSection.md +54 -0
  107. package/docs/components/ui/Breadcrumb.md +70 -0
  108. package/docs/components/ui/Button.md +514 -0
  109. package/docs/components/ui/Card.md +501 -0
  110. package/docs/components/ui/Input.md +54 -0
  111. package/docs/components/ui/MobileLinks.md +43 -0
  112. package/docs/components/ui/Modal.md +60 -0
  113. package/docs/components/ui/Tabs.md +62 -0
  114. package/docs/components/ui/Textarea.md +52 -0
  115. package/docs/core-concepts/configuration-driven.md +552 -0
  116. package/docs/core-concepts/overview.md +351 -0
  117. package/docs/features/accessibility/README.md +73 -0
  118. package/docs/features/accessibility/aria-support.md +177 -0
  119. package/docs/features/accessibility/color-contrast.md +155 -0
  120. package/docs/features/accessibility/focus-management.md +187 -0
  121. package/docs/features/accessibility/testing.md +196 -0
  122. package/docs/features/analytics/README.md +51 -0
  123. package/docs/features/analytics/ab-testing.md +171 -0
  124. package/docs/features/analytics/conversion-tracking.md +207 -0
  125. package/docs/features/analytics/custom-events.md +219 -0
  126. package/docs/features/analytics/privacy.md +198 -0
  127. package/docs/features/analytics/setup.md +114 -0
  128. package/docs/features/analytics/tracking-events.md +224 -0
  129. package/docs/features/i18n/README.md +51 -0
  130. package/docs/features/i18n/best-practices.md +273 -0
  131. package/docs/features/i18n/configuration.md +84 -0
  132. package/docs/features/i18n/formatting.md +133 -0
  133. package/docs/features/i18n/locale-detection.md +122 -0
  134. package/docs/features/i18n/routing.md +99 -0
  135. package/docs/features/i18n/rtl-support.md +191 -0
  136. package/docs/features/i18n/translations.md +129 -0
  137. package/docs/features/internationalization.md +595 -0
  138. package/docs/features/performance/README.md +77 -0
  139. package/docs/features/performance/bundle-size.md +134 -0
  140. package/docs/features/performance/caching.md +131 -0
  141. package/docs/features/performance/code-splitting.md +121 -0
  142. package/docs/features/performance/image-optimization.md +110 -0
  143. package/docs/features/performance/lazy-loading.md +92 -0
  144. package/docs/features/performance/monitoring.md +148 -0
  145. package/docs/features/seo/README.md +51 -0
  146. package/docs/features/seo/best-practices.md +184 -0
  147. package/docs/features/seo/canonical-urls.md +182 -0
  148. package/docs/features/seo/meta-tags.md +126 -0
  149. package/docs/features/seo/open-graph.md +166 -0
  150. package/docs/features/seo/robots-txt.md +146 -0
  151. package/docs/features/seo/sitemaps.md +162 -0
  152. package/docs/features/seo/structured-data.md +166 -0
  153. package/docs/getting-started/installation.md +292 -0
  154. package/docs/getting-started/introduction.md +195 -0
  155. package/docs/getting-started/quick-start.md +460 -0
  156. package/docs/guides/analytics-setup.md +616 -0
  157. package/docs/i18n/CONFIGURATION.md +353 -0
  158. package/docs/i18n/EXAMPLES.md +402 -0
  159. package/docs/i18n/MIGRATION.md +260 -0
  160. package/docs/i18n/SEO.md +392 -0
  161. package/docs/i18n/STATIC-GENERATION-FIX.md +71 -0
  162. package/docs/migration/changelog.md +136 -0
  163. package/docs/migration/overview.md +233 -0
  164. package/docs/recipes/adding-animations.md +475 -0
  165. package/docs/recipes/forms-with-validation.md +393 -0
  166. package/package.json +152 -0
@@ -0,0 +1,708 @@
1
+ # Sitemap Generation Guide
2
+
3
+ Complete guide to generating XML sitemaps for better SEO and search engine crawling.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Quick Start](#quick-start)
9
+ - [Single-Language Sitemaps](#single-language-sitemaps)
10
+ - [Multi-Language Sitemaps](#multi-language-sitemaps)
11
+ - [Configuration Options](#configuration-options)
12
+ - [Next.js Integration](#nextjs-integration)
13
+ - [Dynamic Routes](#dynamic-routes)
14
+ - [Best Practices](#best-practices)
15
+ - [Testing](#testing)
16
+
17
+ ## Overview
18
+
19
+ A sitemap is an XML file that lists all important pages on your website, helping search engines:
20
+
21
+ - **Discover** all your pages efficiently
22
+ - **Understand** your site structure
23
+ - **Crawl** pages in the right order
24
+ - **Index** content faster
25
+ - **Recognize** multi-language pages with hreflang
26
+
27
+ The framework provides utilities to:
28
+ - Generate valid XML sitemaps
29
+ - Support multi-language sites with hreflang
30
+ - Configure priority and change frequency
31
+ - Validate sitemap entries
32
+
33
+ ## Quick Start
34
+
35
+ ### Basic Sitemap
36
+
37
+ Create a simple sitemap for a single-language site:
38
+
39
+ ```tsx
40
+ // app/sitemap.xml/route.ts
41
+ import { generateSitemap, createSitemapEntry } from 'simple-site-framework/lib/seo/sitemap';
42
+
43
+ export async function GET() {
44
+ const sitemap = generateSitemap({
45
+ baseUrl: 'https://acme.com',
46
+ entries: [
47
+ createSitemapEntry('https://acme.com', '/', {
48
+ priority: 1.0,
49
+ changeFrequency: 'weekly'
50
+ }),
51
+ createSitemapEntry('https://acme.com', '/about', {
52
+ priority: 0.8,
53
+ changeFrequency: 'monthly'
54
+ }),
55
+ createSitemapEntry('https://acme.com', '/products', {
56
+ priority: 0.9,
57
+ changeFrequency: 'daily'
58
+ }),
59
+ createSitemapEntry('https://acme.com', '/pricing', {
60
+ priority: 0.9,
61
+ changeFrequency: 'weekly'
62
+ }),
63
+ createSitemapEntry('https://acme.com', '/blog', {
64
+ priority: 0.7,
65
+ changeFrequency: 'daily'
66
+ })
67
+ ]
68
+ });
69
+
70
+ return new Response(sitemap, {
71
+ headers: {
72
+ 'Content-Type': 'application/xml',
73
+ 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate'
74
+ }
75
+ });
76
+ }
77
+ ```
78
+
79
+ This creates: `https://acme.com/sitemap.xml`
80
+
81
+ ## Single-Language Sitemaps
82
+
83
+ ### Manual Entry Creation
84
+
85
+ ```tsx
86
+ import { createSitemapEntry } from 'simple-site-framework/lib/seo/sitemap';
87
+
88
+ const entries = [
89
+ // Homepage - highest priority
90
+ createSitemapEntry('https://acme.com', '/', {
91
+ priority: 1.0,
92
+ changeFrequency: 'weekly',
93
+ lastModified: new Date('2024-01-15')
94
+ }),
95
+
96
+ // Key product pages
97
+ createSitemapEntry('https://acme.com', '/pricing', {
98
+ priority: 0.9,
99
+ changeFrequency: 'weekly'
100
+ }),
101
+ createSitemapEntry('https://acme.com', '/features', {
102
+ priority: 0.9,
103
+ changeFrequency: 'monthly'
104
+ }),
105
+
106
+ // About/contact
107
+ createSitemapEntry('https://acme.com', '/about', {
108
+ priority: 0.7,
109
+ changeFrequency: 'monthly'
110
+ }),
111
+ createSitemapEntry('https://acme.com', '/contact', {
112
+ priority: 0.6,
113
+ changeFrequency: 'yearly'
114
+ }),
115
+
116
+ // Blog - frequently updated
117
+ createSitemapEntry('https://acme.com', '/blog', {
118
+ priority: 0.7,
119
+ changeFrequency: 'daily'
120
+ })
121
+ ];
122
+ ```
123
+
124
+ ### With Last Modified Dates
125
+
126
+ ```tsx
127
+ const entries = [
128
+ createSitemapEntry('https://acme.com', '/', {
129
+ priority: 1.0,
130
+ lastModified: new Date(), // Current date
131
+ changeFrequency: 'weekly'
132
+ }),
133
+ createSitemapEntry('https://acme.com', '/blog/post-1', {
134
+ priority: 0.6,
135
+ lastModified: '2024-01-15T10:00:00Z', // ISO 8601 string
136
+ changeFrequency: 'never'
137
+ })
138
+ ];
139
+ ```
140
+
141
+ ## Multi-Language Sitemaps
142
+
143
+ ### Using createMultiLanguageEntries()
144
+
145
+ Generate entries for pages that exist in multiple languages:
146
+
147
+ ```tsx
148
+ import {
149
+ generateSitemap,
150
+ createMultiLanguageEntries
151
+ } from 'simple-site-framework/lib/seo/sitemap';
152
+
153
+ const locales = ['en', 'fr', 'es'];
154
+ const baseUrl = 'https://acme.com';
155
+
156
+ const entries = [
157
+ // Homepage in all languages
158
+ ...createMultiLanguageEntries(
159
+ baseUrl,
160
+ '/',
161
+ locales,
162
+ 'en', // Default locale for x-default
163
+ { priority: 1.0, changeFrequency: 'weekly' }
164
+ ),
165
+
166
+ // About page in all languages
167
+ ...createMultiLanguageEntries(
168
+ baseUrl,
169
+ '/about',
170
+ locales,
171
+ 'en',
172
+ { priority: 0.8, changeFrequency: 'monthly' }
173
+ ),
174
+
175
+ // Pricing page in all languages
176
+ ...createMultiLanguageEntries(
177
+ baseUrl,
178
+ '/pricing',
179
+ locales,
180
+ 'en',
181
+ { priority: 0.9, changeFrequency: 'weekly' }
182
+ )
183
+ ];
184
+
185
+ const sitemap = generateSitemap({ baseUrl, entries });
186
+ ```
187
+
188
+ This creates entries like:
189
+ ```xml
190
+ <url>
191
+ <loc>https://acme.com/en/about</loc>
192
+ <xhtml:link rel="alternate" hreflang="en" href="https://acme.com/en/about" />
193
+ <xhtml:link rel="alternate" hreflang="fr" href="https://acme.com/fr/about" />
194
+ <xhtml:link rel="alternate" hreflang="es" href="https://acme.com/es/about" />
195
+ <xhtml:link rel="alternate" hreflang="x-default" href="https://acme.com/en/about" />
196
+ <priority>0.8</priority>
197
+ <changefreq>monthly</changefreq>
198
+ </url>
199
+ ```
200
+
201
+ ### Manual Multi-Language Entries
202
+
203
+ For more control, create entries manually:
204
+
205
+ ```tsx
206
+ const entries = [
207
+ {
208
+ url: 'https://acme.com/en/about',
209
+ priority: 0.8,
210
+ alternates: [
211
+ { hreflang: 'en', href: 'https://acme.com/en/about' },
212
+ { hreflang: 'fr', href: 'https://acme.com/fr/about' },
213
+ { hreflang: 'es', href: 'https://acme.com/es/about' },
214
+ { hreflang: 'x-default', href: 'https://acme.com/en/about' }
215
+ ]
216
+ },
217
+ {
218
+ url: 'https://acme.com/fr/about',
219
+ priority: 0.8,
220
+ alternates: [
221
+ { hreflang: 'en', href: 'https://acme.com/en/about' },
222
+ { hreflang: 'fr', href: 'https://acme.com/fr/about' },
223
+ { hreflang: 'es', href: 'https://acme.com/es/about' },
224
+ { hreflang: 'x-default', href: 'https://acme.com/en/about' }
225
+ ]
226
+ }
227
+ ];
228
+ ```
229
+
230
+ ## Configuration Options
231
+
232
+ ### Priority (0.0 - 1.0)
233
+
234
+ Indicates the importance of a page relative to other pages on your site:
235
+
236
+ ```tsx
237
+ createSitemapEntry(baseUrl, '/', { priority: 1.0 }) // Homepage - highest
238
+ createSitemapEntry(baseUrl, '/pricing', { priority: 0.9 }) // Key pages
239
+ createSitemapEntry(baseUrl, '/about', { priority: 0.7 }) // Secondary pages
240
+ createSitemapEntry(baseUrl, '/terms', { priority: 0.3 }) // Legal pages
241
+ ```
242
+
243
+ **Recommended priorities:**
244
+ - 1.0: Homepage
245
+ - 0.9: Key product/service pages
246
+ - 0.7-0.8: Important content pages
247
+ - 0.5-0.6: Blog posts, articles
248
+ - 0.3-0.4: Legal pages, archives
249
+
250
+ ### Change Frequency
251
+
252
+ How often the page is likely to change:
253
+
254
+ ```tsx
255
+ changeFrequency: 'always' // Changes every time it's accessed (rare)
256
+ changeFrequency: 'hourly' // Real-time data, live scores
257
+ changeFrequency: 'daily' // News, blog homepage, active content
258
+ changeFrequency: 'weekly' // Homepage, pricing, features
259
+ changeFrequency: 'monthly' // About, team, documentation
260
+ changeFrequency: 'yearly' // Terms, privacy, legal pages
261
+ changeFrequency: 'never' // Archived content
262
+ ```
263
+
264
+ **Note:** This is a *hint* to search engines, not a directive.
265
+
266
+ ### Last Modified
267
+
268
+ When the page was last changed:
269
+
270
+ ```tsx
271
+ // Using Date object
272
+ lastModified: new Date()
273
+ lastModified: new Date('2024-01-15')
274
+
275
+ // Using ISO 8601 string
276
+ lastModified: '2024-01-15T10:00:00Z'
277
+ lastModified: '2024-01-15'
278
+ ```
279
+
280
+ ### Pretty Print
281
+
282
+ Format XML for human readability:
283
+
284
+ ```tsx
285
+ const sitemap = generateSitemap({
286
+ baseUrl: 'https://acme.com',
287
+ entries: [...],
288
+ prettyPrint: true // Adds indentation and line breaks
289
+ });
290
+ ```
291
+
292
+ **Use pretty print for:**
293
+ - Development/debugging
294
+ - Manual inspection
295
+
296
+ **Don't use pretty print for:**
297
+ - Production (larger file size)
298
+ - When file size matters
299
+
300
+ ## Next.js Integration
301
+
302
+ ### App Router (Next.js 13+)
303
+
304
+ Create a Route Handler for the sitemap:
305
+
306
+ ```tsx
307
+ // app/sitemap.xml/route.ts
308
+ import { generateSitemap, createSitemapEntry } from 'simple-site-framework/lib/seo/sitemap';
309
+
310
+ export async function GET() {
311
+ const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://example.com';
312
+
313
+ const staticEntries = [
314
+ createSitemapEntry(baseUrl, '/', { priority: 1.0, changeFrequency: 'weekly' }),
315
+ createSitemapEntry(baseUrl, '/about', { priority: 0.8 }),
316
+ createSitemapEntry(baseUrl, '/pricing', { priority: 0.9 })
317
+ ];
318
+
319
+ const sitemap = generateSitemap({
320
+ baseUrl,
321
+ entries: staticEntries
322
+ });
323
+
324
+ return new Response(sitemap, {
325
+ headers: {
326
+ 'Content-Type': 'application/xml',
327
+ 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate'
328
+ }
329
+ });
330
+ }
331
+ ```
332
+
333
+ ### Multi-Language App Router
334
+
335
+ ```tsx
336
+ // app/sitemap.xml/route.ts
337
+ import { generateSitemap, createMultiLanguageEntries } from 'simple-site-framework/lib/seo/sitemap';
338
+ import { getI18nConfig } from '@/config/i18n';
339
+
340
+ export async function GET() {
341
+ const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://example.com';
342
+ const { locales, defaultLocale } = getI18nConfig();
343
+
344
+ // Static pages
345
+ const staticPages = ['/', '/about', '/pricing', '/features'];
346
+
347
+ const entries = staticPages.flatMap(page =>
348
+ createMultiLanguageEntries(
349
+ baseUrl,
350
+ page,
351
+ locales,
352
+ defaultLocale,
353
+ {
354
+ priority: page === '/' ? 1.0 : 0.8,
355
+ changeFrequency: 'weekly'
356
+ }
357
+ )
358
+ );
359
+
360
+ const sitemap = generateSitemap({ baseUrl, entries });
361
+
362
+ return new Response(sitemap, {
363
+ headers: {
364
+ 'Content-Type': 'application/xml',
365
+ 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate'
366
+ }
367
+ });
368
+ }
369
+ ```
370
+
371
+ ## Dynamic Routes
372
+
373
+ ### Blog Posts
374
+
375
+ Include dynamically generated content:
376
+
377
+ ```tsx
378
+ // app/sitemap.xml/route.ts
379
+ import { generateSitemap, createSitemapEntry } from 'simple-site-framework/lib/seo/sitemap';
380
+
381
+ export async function GET() {
382
+ const baseUrl = 'https://acme.com';
383
+
384
+ // Static pages
385
+ const staticEntries = [
386
+ createSitemapEntry(baseUrl, '/', { priority: 1.0 }),
387
+ createSitemapEntry(baseUrl, '/blog', { priority: 0.7, changeFrequency: 'daily' })
388
+ ];
389
+
390
+ // Fetch blog posts from database/CMS
391
+ const posts = await fetchBlogPosts();
392
+
393
+ const blogEntries = posts.map(post =>
394
+ createSitemapEntry(baseUrl, `/blog/${post.slug}`, {
395
+ priority: 0.6,
396
+ changeFrequency: 'never',
397
+ lastModified: post.updatedAt
398
+ })
399
+ );
400
+
401
+ const sitemap = generateSitemap({
402
+ baseUrl,
403
+ entries: [...staticEntries, ...blogEntries]
404
+ });
405
+
406
+ return new Response(sitemap, {
407
+ headers: {
408
+ 'Content-Type': 'application/xml',
409
+ 'Cache-Control': 'public, s-maxage=3600'
410
+ }
411
+ });
412
+ }
413
+
414
+ async function fetchBlogPosts() {
415
+ // Fetch from your CMS, database, etc.
416
+ return [
417
+ { slug: 'email-tips', updatedAt: '2024-01-15T10:00:00Z' },
418
+ { slug: 'marketing-guide', updatedAt: '2024-01-20T14:30:00Z' }
419
+ ];
420
+ }
421
+ ```
422
+
423
+ ### Multi-Language Dynamic Routes
424
+
425
+ ```tsx
426
+ const posts = await fetchBlogPosts();
427
+
428
+ const blogEntries = posts.flatMap(post =>
429
+ createMultiLanguageEntries(
430
+ baseUrl,
431
+ `/blog/${post.slug}`,
432
+ locales,
433
+ defaultLocale,
434
+ {
435
+ priority: 0.6,
436
+ changeFrequency: 'never',
437
+ lastModified: post.updatedAt
438
+ }
439
+ )
440
+ );
441
+ ```
442
+
443
+ ## Best Practices
444
+
445
+ ### 1. Use Absolute URLs
446
+
447
+ Always use absolute URLs (https://...) never relative (/path):
448
+
449
+ ```tsx
450
+ // ✅ Good
451
+ createSitemapEntry('https://acme.com', '/about')
452
+
453
+ // ❌ Bad
454
+ createSitemapEntry('acme.com', '/about')
455
+ createSitemapEntry('', '/about')
456
+ ```
457
+
458
+ ### 2. Include Important Pages Only
459
+
460
+ Don't include:
461
+ - Login/logout pages
462
+ - Admin pages
463
+ - Thank you pages
464
+ - Confirmation pages
465
+ - Pages with `noindex`
466
+
467
+ ```tsx
468
+ // ✅ Good - Public pages only
469
+ const entries = [
470
+ createSitemapEntry(baseUrl, '/'),
471
+ createSitemapEntry(baseUrl, '/products'),
472
+ createSitemapEntry(baseUrl, '/pricing')
473
+ ];
474
+
475
+ // ❌ Bad - Includes private/irrelevant pages
476
+ const entries = [
477
+ createSitemapEntry(baseUrl, '/admin'),
478
+ createSitemapEntry(baseUrl, '/thank-you'),
479
+ createSitemapEntry(baseUrl, '/cart')
480
+ ];
481
+ ```
482
+
483
+ ### 3. Set Realistic Priorities
484
+
485
+ Don't set everything to 1.0:
486
+
487
+ ```tsx
488
+ // ✅ Good - Varied priorities
489
+ createSitemapEntry(baseUrl, '/', { priority: 1.0 })
490
+ createSitemapEntry(baseUrl, '/pricing', { priority: 0.9 })
491
+ createSitemapEntry(baseUrl, '/blog/post', { priority: 0.6 })
492
+
493
+ // ❌ Bad - Everything is highest priority
494
+ createSitemapEntry(baseUrl, '/', { priority: 1.0 })
495
+ createSitemapEntry(baseUrl, '/terms', { priority: 1.0 })
496
+ createSitemapEntry(baseUrl, '/404', { priority: 1.0 })
497
+ ```
498
+
499
+ ### 4. Use Accurate Change Frequencies
500
+
501
+ Base it on actual update patterns:
502
+
503
+ ```tsx
504
+ // ✅ Good
505
+ createSitemapEntry(baseUrl, '/', { changeFrequency: 'weekly' }) // Homepage updated weekly
506
+ createSitemapEntry(baseUrl, '/blog', { changeFrequency: 'daily' }) // New posts daily
507
+ createSitemapEntry(baseUrl, '/terms', { changeFrequency: 'yearly' }) // Rarely changes
508
+
509
+ // ❌ Bad
510
+ createSitemapEntry(baseUrl, '/terms', { changeFrequency: 'hourly' }) // Unrealistic
511
+ ```
512
+
513
+ ### 5. Keep Sitemap Under 50MB / 50,000 URLs
514
+
515
+ If you have more:
516
+ 1. Create multiple sitemaps
517
+ 2. Use a sitemap index file
518
+
519
+ ```tsx
520
+ // sitemap-index.xml
521
+ <?xml version="1.0" encoding="UTF-8"?>
522
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
523
+ <sitemap>
524
+ <loc>https://acme.com/sitemap-main.xml</loc>
525
+ <lastmod>2024-01-15T10:00:00Z</lastmod>
526
+ </sitemap>
527
+ <sitemap>
528
+ <loc>https://acme.com/sitemap-blog.xml</loc>
529
+ <lastmod>2024-01-20T14:30:00Z</lastmod>
530
+ </sitemap>
531
+ </sitemapindex>
532
+ ```
533
+
534
+ ### 6. Cache Appropriately
535
+
536
+ ```tsx
537
+ return new Response(sitemap, {
538
+ headers: {
539
+ 'Content-Type': 'application/xml',
540
+ // Cache for 1 hour, revalidate in background
541
+ 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate'
542
+ }
543
+ });
544
+ ```
545
+
546
+ ### 7. Submit to Search Engines
547
+
548
+ After deploying your sitemap:
549
+
550
+ 1. **Google Search Console**: Add sitemap URL
551
+ 2. **Bing Webmaster Tools**: Submit sitemap
552
+ 3. **robots.txt**: Reference sitemap
553
+
554
+ ```
555
+ # robots.txt
556
+ User-agent: *
557
+ Allow: /
558
+
559
+ Sitemap: https://acme.com/sitemap.xml
560
+ ```
561
+
562
+ ### 8. Monitor for Errors
563
+
564
+ Check Search Console regularly for:
565
+ - Sitemap parsing errors
566
+ - URL errors
567
+ - Coverage issues
568
+
569
+ ## Testing
570
+
571
+ ### Validate XML
572
+
573
+ ```tsx
574
+ import { validateSitemap } from 'simple-site-framework/lib/seo/sitemap';
575
+
576
+ const config = {
577
+ baseUrl: 'https://acme.com',
578
+ entries: [...]
579
+ };
580
+
581
+ const result = validateSitemap(config);
582
+
583
+ if (!result.isValid) {
584
+ console.error('Sitemap validation errors:', result.errors);
585
+ }
586
+ ```
587
+
588
+ ### Manual Testing
589
+
590
+ 1. Visit `https://yoursite.com/sitemap.xml`
591
+ 2. View source to inspect XML
592
+ 3. Verify all URLs are absolute
593
+ 4. Check hreflang alternates are correct
594
+
595
+ ### Google Search Console
596
+
597
+ 1. Go to: Sitemaps section
598
+ 2. Submit sitemap URL
599
+ 3. Check for errors after processing
600
+
601
+ ### XML Validators
602
+
603
+ - https://www.xml-sitemaps.com/validate-xml-sitemap.html
604
+ - https://xmlvalidation.com/
605
+
606
+ ## Example: Complete Implementation
607
+
608
+ ```tsx
609
+ // app/sitemap.xml/route.ts
610
+ import {
611
+ generateSitemap,
612
+ createSitemapEntry,
613
+ createMultiLanguageEntries,
614
+ validateSitemap
615
+ } from 'simple-site-framework/lib/seo/sitemap';
616
+
617
+ export async function GET() {
618
+ const baseUrl = process.env.NEXT_PUBLIC_BASE_URL!;
619
+ const locales = ['en', 'fr', 'es'];
620
+ const defaultLocale = 'en';
621
+
622
+ // Static pages
623
+ const staticPages = [
624
+ { path: '/', priority: 1.0, changeFreq: 'weekly' as const },
625
+ { path: '/about', priority: 0.8, changeFreq: 'monthly' as const },
626
+ { path: '/pricing', priority: 0.9, changeFreq: 'weekly' as const },
627
+ { path: '/features', priority: 0.9, changeFreq: 'monthly' as const }
628
+ ];
629
+
630
+ const staticEntries = staticPages.flatMap(page =>
631
+ createMultiLanguageEntries(
632
+ baseUrl,
633
+ page.path,
634
+ locales,
635
+ defaultLocale,
636
+ {
637
+ priority: page.priority,
638
+ changeFrequency: page.changeFreq
639
+ }
640
+ )
641
+ );
642
+
643
+ // Dynamic blog posts
644
+ const posts = await fetchBlogPosts();
645
+ const blogEntries = posts.flatMap(post =>
646
+ createMultiLanguageEntries(
647
+ baseUrl,
648
+ `/blog/${post.slug}`,
649
+ locales,
650
+ defaultLocale,
651
+ {
652
+ priority: 0.6,
653
+ changeFrequency: 'never',
654
+ lastModified: post.updatedAt
655
+ }
656
+ )
657
+ );
658
+
659
+ const config = {
660
+ baseUrl,
661
+ entries: [...staticEntries, ...blogEntries]
662
+ };
663
+
664
+ // Validate before generating
665
+ const validation = validateSitemap(config);
666
+ if (!validation.isValid) {
667
+ console.error('Sitemap validation errors:', validation.errors);
668
+ }
669
+
670
+ const sitemap = generateSitemap(config);
671
+
672
+ return new Response(sitemap, {
673
+ headers: {
674
+ 'Content-Type': 'application/xml',
675
+ 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate'
676
+ }
677
+ });
678
+ }
679
+
680
+ async function fetchBlogPosts() {
681
+ // Fetch from database/CMS
682
+ return [];
683
+ }
684
+ ```
685
+
686
+ ## Checklist
687
+
688
+ - [ ] Sitemap accessible at /sitemap.xml
689
+ - [ ] All URLs are absolute (https://)
690
+ - [ ] No duplicate URLs
691
+ - [ ] All URLs return 200 status
692
+ - [ ] Priority values between 0.0 and 1.0
693
+ - [ ] Change frequencies are realistic
694
+ - [ ] Multi-language pages have hreflang alternates
695
+ - [ ] x-default points to default language
696
+ - [ ] Submitted to Google Search Console
697
+ - [ ] Submitted to Bing Webmaster Tools
698
+ - [ ] Referenced in robots.txt
699
+ - [ ] Under 50MB and 50,000 URLs
700
+ - [ ] Appropriate caching headers
701
+ - [ ] No errors in Search Console
702
+
703
+ ## Resources
704
+
705
+ - [Sitemaps.org Protocol](https://www.sitemaps.org/protocol.html)
706
+ - [Google Sitemap Guidelines](https://developers.google.com/search/docs/crawling-indexing/sitemaps/overview)
707
+ - [Google hreflang Guidelines](https://developers.google.com/search/docs/specialty/international/localized-versions)
708
+ - [XML Sitemap Validator](https://www.xml-sitemaps.com/validate-xml-sitemap.html)