@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,134 @@
|
|
|
1
|
+
# Bundle Size
|
|
2
|
+
|
|
3
|
+
Minimize JavaScript sent to the browser.
|
|
4
|
+
|
|
5
|
+
## Why Bundle Size Matters
|
|
6
|
+
|
|
7
|
+
Every kilobyte of JavaScript:
|
|
8
|
+
- Must be downloaded (affects slow connections)
|
|
9
|
+
- Must be parsed and compiled (affects slow devices)
|
|
10
|
+
- Delays interactivity (affects FID/INP metrics)
|
|
11
|
+
|
|
12
|
+
## Framework Bundle Impact
|
|
13
|
+
|
|
14
|
+
The framework minimizes client-side JavaScript through Server Components:
|
|
15
|
+
|
|
16
|
+
| Component Type | Client JS Impact |
|
|
17
|
+
|---------------|-----------------|
|
|
18
|
+
| Server Components (most) | 0 KB |
|
|
19
|
+
| Client Components (interactive) | Varies |
|
|
20
|
+
|
|
21
|
+
### Zero-JS Components
|
|
22
|
+
|
|
23
|
+
These ship no JavaScript to the browser:
|
|
24
|
+
- HeroSection, FeaturesGrid, ServicesSection
|
|
25
|
+
- ContactSection, AboutSection, CTASection
|
|
26
|
+
- Header, Footer, Card, Button
|
|
27
|
+
- SEOMetaTags, StructuredData, I18nMetaTags
|
|
28
|
+
|
|
29
|
+
### Client Components
|
|
30
|
+
|
|
31
|
+
These add to the bundle:
|
|
32
|
+
- AnalyticsTracker, TrackedLink
|
|
33
|
+
- AnimatedSection, AnimatedCounter
|
|
34
|
+
- ExitIntentModal, CountdownTimer
|
|
35
|
+
- ContactForm (validation, submission)
|
|
36
|
+
- LanguageSelector (interaction)
|
|
37
|
+
|
|
38
|
+
## Measuring Bundle Size
|
|
39
|
+
|
|
40
|
+
### Next.js Build Output
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm run build
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Check the output for page sizes:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Route (app) Size First Load JS
|
|
50
|
+
┌ ○ / 5.2 kB 89 kB
|
|
51
|
+
├ ○ /about 1.8 kB 85.6 kB
|
|
52
|
+
├ ○ /services 3.1 kB 87 kB
|
|
53
|
+
└ ○ /contact 4.5 kB 88.3 kB
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Bundle Analyzer
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm install @next/bundle-analyzer
|
|
60
|
+
|
|
61
|
+
# Run analysis
|
|
62
|
+
ANALYZE=true npm run build
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Import Cost (VS Code)
|
|
66
|
+
|
|
67
|
+
Install the "Import Cost" extension to see package sizes inline.
|
|
68
|
+
|
|
69
|
+
## Reducing Bundle Size
|
|
70
|
+
|
|
71
|
+
### 1. Audit Imports
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// ❌ Bad - imports entire library
|
|
75
|
+
import _ from 'lodash';
|
|
76
|
+
_.debounce(fn, 300);
|
|
77
|
+
|
|
78
|
+
// ✅ Good - imports only what's needed
|
|
79
|
+
import debounce from 'lodash/debounce';
|
|
80
|
+
debounce(fn, 300);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. Replace Heavy Libraries
|
|
84
|
+
|
|
85
|
+
| Heavy Library | Lighter Alternative |
|
|
86
|
+
|--------------|-------------------|
|
|
87
|
+
| moment.js (300KB) | date-fns or native Intl |
|
|
88
|
+
| lodash (70KB) | Individual lodash functions |
|
|
89
|
+
| jQuery (90KB) | Native DOM APIs |
|
|
90
|
+
|
|
91
|
+
### 3. Dynamic Import Heavy Dependencies
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// Only load when needed
|
|
95
|
+
const { default: heavyLib } = await import('heavy-library');
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 4. Use Server Components
|
|
99
|
+
|
|
100
|
+
Move data fetching and rendering to the server:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// ✅ Server Component - no client JS
|
|
104
|
+
export default async function Page() {
|
|
105
|
+
const data = await fetchData();
|
|
106
|
+
return <DataDisplay data={data} />;
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 5. Check for Duplicates
|
|
111
|
+
|
|
112
|
+
Multiple versions of the same package inflate bundles:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Check for duplicates
|
|
116
|
+
npm ls react
|
|
117
|
+
npm ls react-dom
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Budget Guidelines
|
|
121
|
+
|
|
122
|
+
Suggested bundle size budgets:
|
|
123
|
+
|
|
124
|
+
| Metric | Budget |
|
|
125
|
+
|--------|--------|
|
|
126
|
+
| Total JS (initial load) | < 100 KB (gzipped) |
|
|
127
|
+
| Per-page JS | < 30 KB (gzipped) |
|
|
128
|
+
| Largest single dependency | < 50 KB (gzipped) |
|
|
129
|
+
|
|
130
|
+
## See Also
|
|
131
|
+
|
|
132
|
+
- [Code Splitting](./code-splitting.md)
|
|
133
|
+
- [Monitoring](./monitoring.md)
|
|
134
|
+
- [Existing Performance Documentation](../../PERFORMANCE.md)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Caching
|
|
2
|
+
|
|
3
|
+
Caching strategies for faster page delivery.
|
|
4
|
+
|
|
5
|
+
## Static Generation (Default)
|
|
6
|
+
|
|
7
|
+
By default, Next.js App Router statically generates pages at build time:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// This page is static - generated once at build time
|
|
11
|
+
export default function AboutPage() {
|
|
12
|
+
return (
|
|
13
|
+
<HeroSection heading="About Us" />
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Static pages are served from CDN with no server computation.
|
|
19
|
+
|
|
20
|
+
## Revalidation
|
|
21
|
+
|
|
22
|
+
For content that changes periodically:
|
|
23
|
+
|
|
24
|
+
### Time-Based Revalidation
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// Revalidate every hour
|
|
28
|
+
export const revalidate = 3600;
|
|
29
|
+
|
|
30
|
+
export default async function BlogPage() {
|
|
31
|
+
const posts = await getPosts();
|
|
32
|
+
return <BlogList posts={posts} />;
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### On-Demand Revalidation
|
|
37
|
+
|
|
38
|
+
Revalidate when content changes:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// app/api/revalidate/route.ts
|
|
42
|
+
import { revalidatePath } from 'next/cache';
|
|
43
|
+
|
|
44
|
+
export async function POST(request: Request) {
|
|
45
|
+
const { path } = await request.json();
|
|
46
|
+
revalidatePath(path);
|
|
47
|
+
return Response.json({ revalidated: true });
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Fetch Caching
|
|
52
|
+
|
|
53
|
+
Next.js caches fetch requests by default:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Cached indefinitely (static)
|
|
57
|
+
const data = await fetch('https://api.example.com/data');
|
|
58
|
+
|
|
59
|
+
// Revalidate every 60 seconds
|
|
60
|
+
const data = await fetch('https://api.example.com/data', {
|
|
61
|
+
next: { revalidate: 60 },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Never cache
|
|
65
|
+
const data = await fetch('https://api.example.com/data', {
|
|
66
|
+
cache: 'no-store',
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Browser Caching
|
|
71
|
+
|
|
72
|
+
### Cache Headers
|
|
73
|
+
|
|
74
|
+
Configure in next.config.js:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
module.exports = {
|
|
78
|
+
async headers() {
|
|
79
|
+
return [
|
|
80
|
+
{
|
|
81
|
+
source: '/images/:path*',
|
|
82
|
+
headers: [
|
|
83
|
+
{
|
|
84
|
+
key: 'Cache-Control',
|
|
85
|
+
value: 'public, max-age=31536000, immutable',
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
source: '/:path*',
|
|
91
|
+
headers: [
|
|
92
|
+
{
|
|
93
|
+
key: 'Cache-Control',
|
|
94
|
+
value: 'public, max-age=0, must-revalidate',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Asset Caching
|
|
104
|
+
|
|
105
|
+
Next.js automatically:
|
|
106
|
+
- Hashes static assets (JS, CSS) for cache busting
|
|
107
|
+
- Sets long cache headers on hashed files
|
|
108
|
+
- Serves images through optimized pipeline
|
|
109
|
+
|
|
110
|
+
## CDN Caching
|
|
111
|
+
|
|
112
|
+
When deployed to Vercel or similar platforms:
|
|
113
|
+
- Static pages cached at edge globally
|
|
114
|
+
- API routes cached based on headers
|
|
115
|
+
- Images optimized and cached at edge
|
|
116
|
+
|
|
117
|
+
## Caching Strategy by Content Type
|
|
118
|
+
|
|
119
|
+
| Content Type | Strategy | Revalidation |
|
|
120
|
+
|-------------|----------|--------------|
|
|
121
|
+
| Static pages (About, Contact) | Static generation | On deploy |
|
|
122
|
+
| Blog posts | ISR | Every 1-24 hours |
|
|
123
|
+
| Dynamic data (prices, stock) | No cache or short ISR | 1-5 minutes |
|
|
124
|
+
| Images | Long cache | On change |
|
|
125
|
+
| JS/CSS bundles | Immutable | On deploy (hashed) |
|
|
126
|
+
|
|
127
|
+
## See Also
|
|
128
|
+
|
|
129
|
+
- [Code Splitting](./code-splitting.md)
|
|
130
|
+
- [Monitoring](./monitoring.md)
|
|
131
|
+
- [Next.js Caching Documentation](https://nextjs.org/docs/app/building-your-application/caching)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Code Splitting
|
|
2
|
+
|
|
3
|
+
Split JavaScript bundles for faster initial page loads.
|
|
4
|
+
|
|
5
|
+
## Automatic Code Splitting
|
|
6
|
+
|
|
7
|
+
Next.js automatically splits code by route. Each page only loads the JavaScript it needs.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
/ → homepage bundle
|
|
11
|
+
/about → about bundle
|
|
12
|
+
/services → services bundle
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Users visiting `/about` don't download JavaScript for `/services`.
|
|
16
|
+
|
|
17
|
+
## Server vs Client Components
|
|
18
|
+
|
|
19
|
+
The most impactful code splitting is the Server/Client component split:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// Server Component (default) - ZERO client JS
|
|
23
|
+
import { HeroSection } from '@zoyth/simple-site-framework/components';
|
|
24
|
+
|
|
25
|
+
// Client Component - adds to client bundle
|
|
26
|
+
'use client';
|
|
27
|
+
import { AnalyticsTracker } from '@zoyth/simple-site-framework/client';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Most framework components are Server Components, keeping the client bundle small.
|
|
31
|
+
|
|
32
|
+
## Dynamic Imports
|
|
33
|
+
|
|
34
|
+
Load heavy components on demand:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import dynamic from 'next/dynamic';
|
|
38
|
+
|
|
39
|
+
// Only loaded when rendered
|
|
40
|
+
const HeavyChart = dynamic(() => import('../components/HeavyChart'), {
|
|
41
|
+
loading: () => <Skeleton height={300} />,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Only loaded on client (no SSR)
|
|
45
|
+
const InteractiveMap = dynamic(() => import('../components/Map'), {
|
|
46
|
+
ssr: false,
|
|
47
|
+
loading: () => <Skeleton height={400} />,
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## When to Use Dynamic Imports
|
|
52
|
+
|
|
53
|
+
**Good candidates:**
|
|
54
|
+
- Charts and data visualizations
|
|
55
|
+
- Rich text editors
|
|
56
|
+
- Maps
|
|
57
|
+
- Video players
|
|
58
|
+
- Code editors
|
|
59
|
+
- Large forms not immediately visible
|
|
60
|
+
|
|
61
|
+
**Don't dynamically import:**
|
|
62
|
+
- Above-the-fold content
|
|
63
|
+
- Navigation components
|
|
64
|
+
- Small, frequently-used components
|
|
65
|
+
|
|
66
|
+
## Lazy Loading Routes
|
|
67
|
+
|
|
68
|
+
For rarely-visited routes, use dynamic routes:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// app/admin/page.tsx - only loaded when visiting /admin
|
|
72
|
+
export default function AdminPage() {
|
|
73
|
+
// Admin-specific code stays in this bundle
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Analyzing Bundle Size
|
|
78
|
+
|
|
79
|
+
See what's in your bundles:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Build with bundle analyzer
|
|
83
|
+
ANALYZE=true npm run build
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Configure in next.config.js:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
|
90
|
+
enabled: process.env.ANALYZE === 'true',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
module.exports = withBundleAnalyzer({
|
|
94
|
+
// Next.js config...
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Framework Import Patterns
|
|
99
|
+
|
|
100
|
+
### Specific Imports (Recommended)
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// ✅ Only imports what you use
|
|
104
|
+
import { HeroSection } from '@zoyth/simple-site-framework/components';
|
|
105
|
+
import { trackEvent } from '@zoyth/simple-site-framework/client';
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Barrel Imports
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// ⚠️ May include unused code if tree-shaking isn't perfect
|
|
112
|
+
import { HeroSection, FeaturesGrid, ContactSection } from '@zoyth/simple-site-framework/components';
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The framework supports tree-shaking, but specific imports are safer.
|
|
116
|
+
|
|
117
|
+
## See Also
|
|
118
|
+
|
|
119
|
+
- [Bundle Size](./bundle-size.md)
|
|
120
|
+
- [Lazy Loading](./lazy-loading.md)
|
|
121
|
+
- [LazySection Component](../../components/LazySection.md)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Image Optimization
|
|
2
|
+
|
|
3
|
+
Serve optimized images for faster page loads.
|
|
4
|
+
|
|
5
|
+
## Next.js Image Component
|
|
6
|
+
|
|
7
|
+
Use the built-in Image component for automatic optimization:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import Image from 'next/image';
|
|
11
|
+
|
|
12
|
+
<Image
|
|
13
|
+
src="/hero-photo.jpg"
|
|
14
|
+
alt="Team working together"
|
|
15
|
+
width={1200}
|
|
16
|
+
height={600}
|
|
17
|
+
priority // Load immediately for above-fold images
|
|
18
|
+
/>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Priority Images
|
|
22
|
+
|
|
23
|
+
Mark above-the-fold images as priority to preload them:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// ✅ Hero image - above fold
|
|
27
|
+
<Image src="/hero.jpg" alt="..." width={1200} height={600} priority />
|
|
28
|
+
|
|
29
|
+
// Regular image - below fold (lazy loaded by default)
|
|
30
|
+
<Image src="/team.jpg" alt="..." width={800} height={400} />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Responsive Images
|
|
34
|
+
|
|
35
|
+
Serve different sizes based on viewport:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
<Image
|
|
39
|
+
src="/hero.jpg"
|
|
40
|
+
alt="Hero image"
|
|
41
|
+
width={1200}
|
|
42
|
+
height={600}
|
|
43
|
+
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 1200px"
|
|
44
|
+
/>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Image Formats
|
|
48
|
+
|
|
49
|
+
Next.js automatically serves modern formats (WebP, AVIF) when supported by the browser. No configuration needed.
|
|
50
|
+
|
|
51
|
+
## External Images
|
|
52
|
+
|
|
53
|
+
Configure allowed domains for external images:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// next.config.js
|
|
57
|
+
module.exports = {
|
|
58
|
+
images: {
|
|
59
|
+
remotePatterns: [
|
|
60
|
+
{
|
|
61
|
+
protocol: 'https',
|
|
62
|
+
hostname: 'images.unsplash.com',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
protocol: 'https',
|
|
66
|
+
hostname: 'cdn.example.com',
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Background Images
|
|
74
|
+
|
|
75
|
+
For CSS background images, optimize manually:
|
|
76
|
+
|
|
77
|
+
```css
|
|
78
|
+
.hero {
|
|
79
|
+
background-image: url('/hero-bg.webp');
|
|
80
|
+
background-size: cover;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or use a picture element for responsive backgrounds.
|
|
85
|
+
|
|
86
|
+
## Image Sizing Guidelines
|
|
87
|
+
|
|
88
|
+
| Usage | Recommended Size | Format |
|
|
89
|
+
|-------|-----------------|--------|
|
|
90
|
+
| Hero banner | 1920 x 1080 | JPEG/WebP |
|
|
91
|
+
| Card thumbnail | 400 x 300 | JPEG/WebP |
|
|
92
|
+
| Logo | 200 x 60 | SVG/PNG |
|
|
93
|
+
| Icon | 24 x 24 to 48 x 48 | SVG |
|
|
94
|
+
| Open Graph | 1200 x 630 | JPEG/PNG |
|
|
95
|
+
| Favicon | 32 x 32 | ICO/PNG |
|
|
96
|
+
|
|
97
|
+
## Best Practices
|
|
98
|
+
|
|
99
|
+
- Always set `width` and `height` to prevent layout shift (CLS)
|
|
100
|
+
- Use `priority` for above-fold images only
|
|
101
|
+
- Use `sizes` prop for responsive layouts
|
|
102
|
+
- Prefer SVG for logos and icons
|
|
103
|
+
- Compress images before adding to project
|
|
104
|
+
- Use descriptive alt text for accessibility and SEO
|
|
105
|
+
|
|
106
|
+
## See Also
|
|
107
|
+
|
|
108
|
+
- [Lazy Loading](./lazy-loading.md)
|
|
109
|
+
- [Bundle Size](./bundle-size.md)
|
|
110
|
+
- [Next.js Image Documentation](https://nextjs.org/docs/app/api-reference/components/image)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Lazy Loading
|
|
2
|
+
|
|
3
|
+
Defer loading of non-critical content for faster initial page load.
|
|
4
|
+
|
|
5
|
+
## LazySection Component
|
|
6
|
+
|
|
7
|
+
Wrap sections that appear below the fold:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { LazySection } from '@zoyth/simple-site-framework/components';
|
|
11
|
+
|
|
12
|
+
export default function HomePage() {
|
|
13
|
+
return (
|
|
14
|
+
<>
|
|
15
|
+
{/* Above fold - loads immediately */}
|
|
16
|
+
<HeroSection heading="Welcome" />
|
|
17
|
+
|
|
18
|
+
{/* Below fold - lazy loaded on scroll */}
|
|
19
|
+
<LazySection>
|
|
20
|
+
<FeaturesGrid features={features} />
|
|
21
|
+
</LazySection>
|
|
22
|
+
|
|
23
|
+
<LazySection>
|
|
24
|
+
<TestimonialSection testimonials={testimonials} />
|
|
25
|
+
</LazySection>
|
|
26
|
+
|
|
27
|
+
<LazySection>
|
|
28
|
+
<ContactSection />
|
|
29
|
+
</LazySection>
|
|
30
|
+
</>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## How It Works
|
|
36
|
+
|
|
37
|
+
LazySection uses Intersection Observer to detect when content enters the viewport. The wrapped content is rendered only when it becomes visible (or is about to become visible).
|
|
38
|
+
|
|
39
|
+
## Props
|
|
40
|
+
|
|
41
|
+
| Prop | Type | Default | Description |
|
|
42
|
+
|------|------|---------|-------------|
|
|
43
|
+
| `children` | `ReactNode` | - | Content to lazy load |
|
|
44
|
+
| `threshold` | `number` | `0.1` | Visibility threshold (0-1) before loading |
|
|
45
|
+
| `rootMargin` | `string` | `'200px'` | Margin around viewport to preload |
|
|
46
|
+
| `fallback` | `ReactNode` | `null` | Placeholder while loading |
|
|
47
|
+
|
|
48
|
+
## With Skeleton Loading
|
|
49
|
+
|
|
50
|
+
Show placeholder while content loads:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
<LazySection fallback={<Skeleton height={400} />}>
|
|
54
|
+
<TestimonialSection testimonials={testimonials} />
|
|
55
|
+
</LazySection>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Dynamic Imports
|
|
59
|
+
|
|
60
|
+
For heavy client components, combine with Next.js dynamic imports:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import dynamic from 'next/dynamic';
|
|
64
|
+
|
|
65
|
+
const HeavyChart = dynamic(() => import('./HeavyChart'), {
|
|
66
|
+
loading: () => <Skeleton height={300} />,
|
|
67
|
+
ssr: false,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
<LazySection>
|
|
71
|
+
<HeavyChart data={data} />
|
|
72
|
+
</LazySection>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## When to Use
|
|
76
|
+
|
|
77
|
+
**Use LazySection for:**
|
|
78
|
+
- Content below the initial viewport
|
|
79
|
+
- Heavy sections with many images
|
|
80
|
+
- Sections with complex animations
|
|
81
|
+
- Testimonial carousels, image galleries
|
|
82
|
+
|
|
83
|
+
**Don't use LazySection for:**
|
|
84
|
+
- Hero section (above fold)
|
|
85
|
+
- Critical navigation elements
|
|
86
|
+
- Content needed for SEO (search engines may not scroll)
|
|
87
|
+
|
|
88
|
+
## See Also
|
|
89
|
+
|
|
90
|
+
- [LazySection Component](../../components/LazySection.md)
|
|
91
|
+
- [Skeleton Component](../../components/Skeleton.md)
|
|
92
|
+
- [Code Splitting](./code-splitting.md)
|