@zigrivers/scaffold 3.5.3 → 3.7.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/README.md +150 -8
- package/content/knowledge/backend/backend-api-design.md +103 -0
- package/content/knowledge/backend/backend-architecture.md +100 -0
- package/content/knowledge/backend/backend-async-patterns.md +101 -0
- package/content/knowledge/backend/backend-auth-patterns.md +100 -0
- package/content/knowledge/backend/backend-conventions.md +105 -0
- package/content/knowledge/backend/backend-data-modeling.md +102 -0
- package/content/knowledge/backend/backend-deployment.md +100 -0
- package/content/knowledge/backend/backend-dev-environment.md +102 -0
- package/content/knowledge/backend/backend-observability.md +102 -0
- package/content/knowledge/backend/backend-project-structure.md +100 -0
- package/content/knowledge/backend/backend-requirements.md +103 -0
- package/content/knowledge/backend/backend-security.md +104 -0
- package/content/knowledge/backend/backend-testing.md +101 -0
- package/content/knowledge/backend/backend-worker-patterns.md +100 -0
- package/content/knowledge/cli/cli-architecture.md +101 -0
- package/content/knowledge/cli/cli-conventions.md +117 -0
- package/content/knowledge/cli/cli-dev-environment.md +121 -0
- package/content/knowledge/cli/cli-distribution-patterns.md +106 -0
- package/content/knowledge/cli/cli-interactivity-patterns.md +116 -0
- package/content/knowledge/cli/cli-output-patterns.md +107 -0
- package/content/knowledge/cli/cli-project-structure.md +124 -0
- package/content/knowledge/cli/cli-requirements.md +101 -0
- package/content/knowledge/cli/cli-shell-integration.md +130 -0
- package/content/knowledge/cli/cli-testing.md +134 -0
- package/content/knowledge/web-app/web-app-api-patterns.md +224 -0
- package/content/knowledge/web-app/web-app-architecture.md +116 -0
- package/content/knowledge/web-app/web-app-auth-patterns.md +256 -0
- package/content/knowledge/web-app/web-app-conventions.md +121 -0
- package/content/knowledge/web-app/web-app-data-patterns.md +218 -0
- package/content/knowledge/web-app/web-app-deployment-workflow.md +143 -0
- package/content/knowledge/web-app/web-app-deployment.md +134 -0
- package/content/knowledge/web-app/web-app-design-system.md +158 -0
- package/content/knowledge/web-app/web-app-dev-environment.md +173 -0
- package/content/knowledge/web-app/web-app-observability.md +221 -0
- package/content/knowledge/web-app/web-app-project-structure.md +160 -0
- package/content/knowledge/web-app/web-app-rendering-strategies.md +133 -0
- package/content/knowledge/web-app/web-app-requirements.md +112 -0
- package/content/knowledge/web-app/web-app-security.md +193 -0
- package/content/knowledge/web-app/web-app-session-patterns.md +214 -0
- package/content/knowledge/web-app/web-app-testing.md +249 -0
- package/content/knowledge/web-app/web-app-ux-patterns.md +162 -0
- package/content/methodology/backend-overlay.yml +73 -0
- package/content/methodology/cli-overlay.yml +69 -0
- package/content/methodology/web-app-overlay.yml +79 -0
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +354 -7
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +136 -0
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/config/schema.d.ts +800 -32
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +48 -5
- package/dist/config/schema.js.map +1 -1
- package/dist/config/schema.test.js +156 -1
- package/dist/config/schema.test.js.map +1 -1
- package/dist/core/assembly/overlay-loader.d.ts.map +1 -1
- package/dist/core/assembly/overlay-loader.js +2 -1
- package/dist/core/assembly/overlay-loader.js.map +1 -1
- package/dist/core/assembly/overlay-loader.test.js +34 -0
- package/dist/core/assembly/overlay-loader.test.js.map +1 -1
- package/dist/e2e/game-pipeline.test.js +1 -0
- package/dist/e2e/game-pipeline.test.js.map +1 -1
- package/dist/e2e/project-type-overlays.test.d.ts +15 -0
- package/dist/e2e/project-type-overlays.test.d.ts.map +1 -0
- package/dist/e2e/project-type-overlays.test.js +534 -0
- package/dist/e2e/project-type-overlays.test.js.map +1 -0
- package/dist/types/config.d.ts +13 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -1
- package/dist/types/index.js.map +1 -1
- package/dist/wizard/questions.d.ts +30 -1
- package/dist/wizard/questions.d.ts.map +1 -1
- package/dist/wizard/questions.js +181 -36
- package/dist/wizard/questions.js.map +1 -1
- package/dist/wizard/questions.test.js +334 -4
- package/dist/wizard/questions.test.js.map +1 -1
- package/dist/wizard/wizard.d.ts +26 -0
- package/dist/wizard/wizard.d.ts.map +1 -1
- package/dist/wizard/wizard.js +31 -2
- package/dist/wizard/wizard.js.map +1 -1
- package/package.json +1 -1
- package/dist/types/wizard.d.ts +0 -14
- package/dist/types/wizard.d.ts.map +0 -1
- package/dist/types/wizard.js +0 -2
- package/dist/types/wizard.js.map +0 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-app-observability
|
|
3
|
+
description: Real User Monitoring, Core Web Vitals tracking, error tracking with Sentry, CDN analytics, custom performance marks, and performance regression alerting
|
|
4
|
+
topics: [web-app, observability, rum, core-web-vitals, sentry, performance, monitoring]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You cannot improve what you cannot measure. Frontend observability is often treated as an afterthought — added after users start complaining — but by then you have no baseline to regress against and no historical data to understand when performance degraded or errors started. Real User Monitoring (RUM) captures the experience of your actual users on their actual devices and networks, which lab-based tests like Lighthouse cannot replicate. Core Web Vitals are Google's standardized metrics for page experience and directly influence search ranking.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
### Real User Monitoring (RUM)
|
|
12
|
+
|
|
13
|
+
RUM collects performance and behavioral data from real user sessions in production. Unlike synthetic monitoring (scheduled tests from a data center), RUM reflects the diversity of real-world conditions: slow Android devices, congested mobile networks, aggressive ad blockers, and geographically distant users.
|
|
14
|
+
|
|
15
|
+
**Key RUM metrics:**
|
|
16
|
+
|
|
17
|
+
- **Core Web Vitals** — Google's page experience signals (see below)
|
|
18
|
+
- **Time to First Byte (TTFB)** — server response latency
|
|
19
|
+
- **First Contentful Paint (FCP)** — when the first content is painted
|
|
20
|
+
- **Time to Interactive (TTI)** — when the page is reliably interactive
|
|
21
|
+
- **Custom marks** — application-specific milestones (e.g., "dashboard data loaded")
|
|
22
|
+
- **Error rate** — JavaScript exceptions per session
|
|
23
|
+
- **Rage clicks** — repeated clicks on non-interactive elements (UX frustration signal)
|
|
24
|
+
- **Dead clicks** — clicks on elements that produce no response
|
|
25
|
+
|
|
26
|
+
**RUM tools:** Vercel Analytics, Datadog RUM, New Relic Browser, Sentry Performance, web-vitals library (self-hosted reporting).
|
|
27
|
+
|
|
28
|
+
### Core Web Vitals
|
|
29
|
+
|
|
30
|
+
Google's three Core Web Vitals measure loading, interactivity, and visual stability:
|
|
31
|
+
|
|
32
|
+
| Metric | Measures | Good | Needs Improvement | Poor |
|
|
33
|
+
|---|---|---|---|---|
|
|
34
|
+
| **LCP** (Largest Contentful Paint) | Load performance | ≤ 2.5s | 2.5–4s | > 4s |
|
|
35
|
+
| **INP** (Interaction to Next Paint) | Responsiveness | ≤ 200ms | 200–500ms | > 500ms |
|
|
36
|
+
| **CLS** (Cumulative Layout Shift) | Visual stability | ≤ 0.1 | 0.1–0.25 | > 0.25 |
|
|
37
|
+
|
|
38
|
+
**Common LCP causes and fixes:**
|
|
39
|
+
- Unoptimized hero images → use `srcset`, WebP/AVIF, CDN, `loading="eager"`, `fetchpriority="high"`
|
|
40
|
+
- Render-blocking CSS/fonts → inline critical CSS, use `font-display: swap`
|
|
41
|
+
- Slow TTFB → edge caching, CDN, server-side optimization
|
|
42
|
+
|
|
43
|
+
**Common CLS causes and fixes:**
|
|
44
|
+
- Images without explicit `width`/`height` attributes → always set dimensions
|
|
45
|
+
- Late-loading ads or embeds → reserve space with aspect-ratio boxes
|
|
46
|
+
- Custom fonts causing text reflow → use `font-display: optional` or `size-adjust`
|
|
47
|
+
- Dynamic content injected above viewport content → append below, not prepend above
|
|
48
|
+
|
|
49
|
+
**INP (replaced FID in 2024):** Measures the responsiveness of all user interactions, not just the first. Long JavaScript tasks (>50ms) block the main thread and inflate INP. Use `scheduler.postTask()`, web workers, and code splitting to break up long tasks.
|
|
50
|
+
|
|
51
|
+
### Error Tracking with Sentry
|
|
52
|
+
|
|
53
|
+
Sentry captures JavaScript exceptions, stack traces, user context, and breadcrumbs (the sequence of events leading to the error). Configure it to filter noise and surface actionable errors.
|
|
54
|
+
|
|
55
|
+
**Critical Sentry configuration:**
|
|
56
|
+
- Source maps for readable stack traces (never ship source maps publicly — upload to Sentry only)
|
|
57
|
+
- User context (attach user ID, plan tier — never PII like email without consent)
|
|
58
|
+
- Release tracking to correlate error spikes with deployments
|
|
59
|
+
- Sampling rate: 100% for errors, 10–20% for performance transactions in high-traffic apps
|
|
60
|
+
|
|
61
|
+
### Performance Regression Alerting
|
|
62
|
+
|
|
63
|
+
The goal is to catch regressions before they reach users or before they compound. Alert on:
|
|
64
|
+
- LCP p75 (75th percentile) increases by more than 300ms from the rolling 7-day average
|
|
65
|
+
- Error rate increases by more than 0.5% from the rolling 24-hour baseline
|
|
66
|
+
- Any new error type appearing in production for the first time
|
|
67
|
+
|
|
68
|
+
## Deep Guidance
|
|
69
|
+
|
|
70
|
+
### Web Vitals Collection
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// web-vitals library — collect and report Core Web Vitals
|
|
74
|
+
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
|
|
75
|
+
|
|
76
|
+
function sendToAnalytics(metric: any) {
|
|
77
|
+
// Batch metrics to avoid sending too many beacons
|
|
78
|
+
const body = JSON.stringify({
|
|
79
|
+
name: metric.name,
|
|
80
|
+
value: metric.value,
|
|
81
|
+
rating: metric.rating, // 'good', 'needs-improvement', 'poor'
|
|
82
|
+
id: metric.id,
|
|
83
|
+
navigationType: metric.navigationType,
|
|
84
|
+
url: window.location.pathname,
|
|
85
|
+
// Attach context for segmentation
|
|
86
|
+
connectionType: (navigator as any).connection?.effectiveType,
|
|
87
|
+
deviceMemory: (navigator as any).deviceMemory,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// sendBeacon is non-blocking and survives page unload
|
|
91
|
+
if (navigator.sendBeacon) {
|
|
92
|
+
navigator.sendBeacon('/api/vitals', body);
|
|
93
|
+
} else {
|
|
94
|
+
fetch('/api/vitals', { method: 'POST', body, keepalive: true });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Register all Core Web Vitals + FCP + TTFB
|
|
99
|
+
onLCP(sendToAnalytics);
|
|
100
|
+
onINP(sendToAnalytics);
|
|
101
|
+
onCLS(sendToAnalytics);
|
|
102
|
+
onFCP(sendToAnalytics);
|
|
103
|
+
onTTFB(sendToAnalytics);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Always use `navigator.sendBeacon` for analytics reporting — it queues the request to be sent after the page unloads without blocking navigation.
|
|
107
|
+
|
|
108
|
+
### Custom Performance Marks
|
|
109
|
+
|
|
110
|
+
Use the User Timing API to measure application-specific milestones that browser APIs cannot capture:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Mark application-level performance milestones
|
|
114
|
+
class PerformanceTracker {
|
|
115
|
+
private marks: Map<string, number> = new Map();
|
|
116
|
+
|
|
117
|
+
mark(name: string): void {
|
|
118
|
+
performance.mark(name);
|
|
119
|
+
this.marks.set(name, performance.now());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
measure(name: string, startMark: string, endMark?: string): number {
|
|
123
|
+
const end = endMark || name + '-end';
|
|
124
|
+
if (!endMark) this.mark(end);
|
|
125
|
+
|
|
126
|
+
const measure = performance.measure(name, startMark, end);
|
|
127
|
+
const duration = measure.duration;
|
|
128
|
+
|
|
129
|
+
// Report to RUM
|
|
130
|
+
this.report({ name, duration });
|
|
131
|
+
return duration;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private report(metric: { name: string; duration: number }): void {
|
|
135
|
+
navigator.sendBeacon?.('/api/perf', JSON.stringify(metric));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const perf = new PerformanceTracker();
|
|
140
|
+
|
|
141
|
+
// Usage in application code
|
|
142
|
+
async function loadDashboard() {
|
|
143
|
+
perf.mark('dashboard-fetch-start');
|
|
144
|
+
const data = await fetchDashboardData();
|
|
145
|
+
perf.mark('dashboard-fetch-end');
|
|
146
|
+
|
|
147
|
+
perf.measure('dashboard-fetch', 'dashboard-fetch-start', 'dashboard-fetch-end');
|
|
148
|
+
|
|
149
|
+
perf.mark('dashboard-render-start');
|
|
150
|
+
renderDashboard(data);
|
|
151
|
+
perf.mark('dashboard-render-end');
|
|
152
|
+
|
|
153
|
+
perf.measure('dashboard-render', 'dashboard-render-start', 'dashboard-render-end');
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
These custom marks appear in Chrome DevTools Performance panel and can be reported to your RUM backend for tracking.
|
|
158
|
+
|
|
159
|
+
### Sentry Setup for Next.js
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// sentry.client.config.ts
|
|
163
|
+
import * as Sentry from '@sentry/nextjs';
|
|
164
|
+
|
|
165
|
+
Sentry.init({
|
|
166
|
+
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
167
|
+
environment: process.env.NEXT_PUBLIC_ENV,
|
|
168
|
+
|
|
169
|
+
// Performance monitoring sample rate — adjust based on traffic volume
|
|
170
|
+
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
|
|
171
|
+
|
|
172
|
+
// Replay for error sessions only (privacy-conscious default)
|
|
173
|
+
replaysOnErrorSampleRate: 1.0,
|
|
174
|
+
replaysSessionSampleRate: 0.0,
|
|
175
|
+
|
|
176
|
+
// Filter noise — don't track network errors from ad blockers
|
|
177
|
+
ignoreErrors: [
|
|
178
|
+
'ResizeObserver loop limit exceeded',
|
|
179
|
+
'Non-Error promise rejection captured',
|
|
180
|
+
/Loading chunk \d+ failed/,
|
|
181
|
+
],
|
|
182
|
+
|
|
183
|
+
beforeSend(event, hint) {
|
|
184
|
+
// Strip PII from error context
|
|
185
|
+
if (event.user) {
|
|
186
|
+
delete event.user.email;
|
|
187
|
+
delete event.user.username;
|
|
188
|
+
}
|
|
189
|
+
return event;
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
integrations: [
|
|
193
|
+
Sentry.replayIntegration({
|
|
194
|
+
maskAllText: true, // Privacy: mask all text in replays
|
|
195
|
+
blockAllMedia: true, // Privacy: block media in replays
|
|
196
|
+
}),
|
|
197
|
+
],
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### CDN Analytics and Cache Hit Rate
|
|
202
|
+
|
|
203
|
+
Monitor CDN cache performance as a leading indicator for LCP and TTFB:
|
|
204
|
+
|
|
205
|
+
- **Cache hit rate** — percentage of requests served from CDN cache vs origin. Target >90% for static assets, >70% for edge-cached HTML.
|
|
206
|
+
- **Origin shield hit rate** — for CDN tiers that include an origin shield, a low rate indicates cold cache or poor cache-control headers.
|
|
207
|
+
- **Edge latency by region** — identify geographic regions where users have poor performance; expand CDN PoP coverage or investigate origin latency.
|
|
208
|
+
|
|
209
|
+
Key cache-control headers:
|
|
210
|
+
```
|
|
211
|
+
# Immutable static assets (hashed filenames)
|
|
212
|
+
Cache-Control: public, max-age=31536000, immutable
|
|
213
|
+
|
|
214
|
+
# HTML pages — revalidate frequently but serve stale while revalidating
|
|
215
|
+
Cache-Control: public, max-age=0, s-maxage=86400, stale-while-revalidate=3600
|
|
216
|
+
|
|
217
|
+
# API responses — no public cache, short browser cache
|
|
218
|
+
Cache-Control: private, max-age=30
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Set `Surrogate-Key` (Fastly) or `Cache-Tag` (Cloudflare) headers on HTML responses to enable targeted cache purging when content changes.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-app-project-structure
|
|
3
|
+
description: Directory layout conventions, route organization, shared vs feature modules, barrel exports, and config file placement for web apps
|
|
4
|
+
topics: [web-app, project-structure, architecture, routing, modules]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
A well-structured web app project is navigable by any developer without a tour. The directory layout should communicate the architecture at a glance: where pages live, where shared code lives, where API logic lives, and where configuration lives. A poor structure forces every developer to ask "where does this go?" on every new file they create.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Web app project structure separates `app/` (routes and layouts), `features/` (domain-owned modules), `components/` (shared UI), `hooks/` (shared hooks), and `lib/` (framework-agnostic utilities). Routes mirror URL structure with thin page files. Feature modules own their components, hooks, and types; shared modules have zero imports from feature modules.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### Standard Directory Layout
|
|
16
|
+
|
|
17
|
+
For a Next.js or similar SSR framework, the canonical structure is:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
src/
|
|
21
|
+
app/ # App Router pages and layouts (Next.js 13+)
|
|
22
|
+
pages/ # Pages Router (Next.js legacy; or Remix/Vite routes)
|
|
23
|
+
components/ # Shared, reusable UI components
|
|
24
|
+
features/ # Feature modules (colocated components + hooks + logic)
|
|
25
|
+
hooks/ # Shared custom hooks used across features
|
|
26
|
+
lib/ # Framework-agnostic utilities and service clients
|
|
27
|
+
api/ # API route handlers (serverless functions)
|
|
28
|
+
types/ # Shared TypeScript types and interfaces
|
|
29
|
+
styles/ # Global CSS, design tokens, theme config
|
|
30
|
+
public/ # Static assets served at root (images, fonts, robots.txt)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For a Vite/React SPA, drop `app/` and `api/` and substitute:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
src/
|
|
37
|
+
routes/ # Route components and nested layouts
|
|
38
|
+
pages/ # Page-level components (one per route)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Route Organization
|
|
42
|
+
|
|
43
|
+
Routes should mirror the URL structure. Avoid flat route files that make the hierarchy ambiguous:
|
|
44
|
+
|
|
45
|
+
- `/dashboard` → `app/dashboard/page.tsx`
|
|
46
|
+
- `/dashboard/settings` → `app/dashboard/settings/page.tsx`
|
|
47
|
+
- `/users/[id]` → `app/users/[id]/page.tsx`
|
|
48
|
+
|
|
49
|
+
Keep route files thin — they orchestrate data loading and pass props to feature components. Business logic belongs in `features/`, not in the page file.
|
|
50
|
+
|
|
51
|
+
**Layouts**: Use layout files (`layout.tsx`) to share navigation, auth checks, and providers across route groups. Do not repeat these in every page file.
|
|
52
|
+
|
|
53
|
+
### Shared vs Feature Modules
|
|
54
|
+
|
|
55
|
+
The critical distinction is "who owns this code":
|
|
56
|
+
|
|
57
|
+
- **Feature module** (`features/checkout/`): Owned by one product domain. Contains everything the checkout feature needs — components, hooks, API calls, types. Other features do not import from it directly; they use shared interfaces.
|
|
58
|
+
- **Shared module** (`components/`, `hooks/`, `lib/`): Owned by no single feature. Used by two or more features. Has no knowledge of any specific feature's business logic.
|
|
59
|
+
|
|
60
|
+
The rule: shared modules must have zero imports from feature modules. Feature modules may import from shared modules. This prevents circular dependencies and keeps shared code reusable.
|
|
61
|
+
|
|
62
|
+
### Barrel Exports
|
|
63
|
+
|
|
64
|
+
Use `index.ts` barrel files at the feature and component directory level to create clean import paths:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// features/user-profile/index.ts
|
|
68
|
+
export { UserProfile } from "./UserProfile";
|
|
69
|
+
export { useUserProfile } from "./useUserProfile";
|
|
70
|
+
export type { UserProfileData } from "./user-profile.types";
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Consumers import from `features/user-profile`, not from deep internal paths:
|
|
74
|
+
```typescript
|
|
75
|
+
// Good
|
|
76
|
+
import { UserProfile } from "@/features/user-profile";
|
|
77
|
+
|
|
78
|
+
// Bad — leaks internal structure
|
|
79
|
+
import { UserProfile } from "@/features/user-profile/components/UserProfile";
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Configure TypeScript path aliases (`@/`) to avoid relative path ladders (`../../../../../../`).
|
|
83
|
+
|
|
84
|
+
### Config Files
|
|
85
|
+
|
|
86
|
+
Config files belong at the repo root, not inside `src/`. Standard placement:
|
|
87
|
+
|
|
88
|
+
- `next.config.ts` / `vite.config.ts` — build and bundler config
|
|
89
|
+
- `tsconfig.json` — TypeScript compiler config
|
|
90
|
+
- `eslint.config.js` — linting rules
|
|
91
|
+
- `.env.example` — documented environment variables (committed); `.env.local` — actual values (gitignored)
|
|
92
|
+
- `tailwind.config.ts` — if using Tailwind
|
|
93
|
+
- `vitest.config.ts` / `jest.config.ts` — test runner config
|
|
94
|
+
|
|
95
|
+
### Feature Module Template
|
|
96
|
+
|
|
97
|
+
Every feature module follows the same internal structure. Establish this template in `CONTRIBUTING.md`:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
features/
|
|
101
|
+
<feature-name>/
|
|
102
|
+
index.ts # Barrel export — public API of the feature
|
|
103
|
+
<FeatureName>.tsx # Top-level feature component
|
|
104
|
+
<FeatureName>.test.tsx # Component tests
|
|
105
|
+
<FeatureName>.stories.tsx # Storybook stories (if applicable)
|
|
106
|
+
use<FeatureName>.ts # Primary data hook
|
|
107
|
+
use<FeatureName>.test.ts # Hook tests
|
|
108
|
+
<feature-name>.types.ts # TypeScript types for this feature
|
|
109
|
+
<feature-name>.api.ts # API calls made by this feature (if applicable)
|
|
110
|
+
components/ # Sub-components used only within this feature
|
|
111
|
+
<SubComponent>.tsx
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Enforce "no cross-feature imports" with ESLint:
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
// eslint-plugin-import rule to prevent cross-feature imports
|
|
118
|
+
"import/no-restricted-paths": [
|
|
119
|
+
"error",
|
|
120
|
+
{
|
|
121
|
+
"zones": [{
|
|
122
|
+
"target": "./src/features",
|
|
123
|
+
"from": "./src/features",
|
|
124
|
+
"except": ["./src/features/index.ts"] // Only barrel is importable
|
|
125
|
+
}]
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Environment Variable Management
|
|
131
|
+
|
|
132
|
+
Never hardcode environment-specific values. Use `.env` files and document every variable:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# .env.example — committed to the repo
|
|
136
|
+
# Never commit .env, .env.local, or .env.production
|
|
137
|
+
|
|
138
|
+
# API base URL
|
|
139
|
+
NEXT_PUBLIC_API_URL=https://api.example.com
|
|
140
|
+
|
|
141
|
+
# Auth provider
|
|
142
|
+
NEXT_PUBLIC_AUTH_DOMAIN=your-domain.auth0.com
|
|
143
|
+
AUTH_SECRET= # Server-only; never expose to client (no NEXT_PUBLIC_ prefix)
|
|
144
|
+
|
|
145
|
+
# Feature flags
|
|
146
|
+
NEXT_PUBLIC_FEATURE_NEW_CHECKOUT=false
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Validate environment variables at startup using a schema (Zod is ideal). Fail fast with a clear error message if required variables are missing or malformed. Never let the app silently run with bad configuration.
|
|
150
|
+
|
|
151
|
+
### Avoiding the "Utils Dumping Ground"
|
|
152
|
+
|
|
153
|
+
A `utils/` directory with no sub-organization becomes a junk drawer within weeks. Apply the same discipline to utilities:
|
|
154
|
+
|
|
155
|
+
- Group by domain: `lib/date.ts`, `lib/currency.ts`, `lib/validation.ts`
|
|
156
|
+
- If a utility grows beyond ~100 lines, give it its own directory with an `index.ts`
|
|
157
|
+
- Delete utilities that are no longer used — dead code in `lib/` is worse than no code
|
|
158
|
+
- Never put business logic in `lib/`. Business logic belongs in features or hooks.
|
|
159
|
+
|
|
160
|
+
The test for whether something belongs in `lib/`: "Could I copy this to a different web app project without modification?" If yes, it belongs in `lib/`. If it has knowledge of this app's domain, it belongs in `features/` or `hooks/`.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-app-rendering-strategies
|
|
3
|
+
description: SSR TTFB and SEO benefits, SSG for content sites, ISR, streaming SSR, React Server Components, and progressive hydration patterns
|
|
4
|
+
topics: [web-app, rendering, ssr, ssg, isr, streaming, react-server-components, hydration]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Rendering strategy is the foundational technical decision in a web app — it determines server infrastructure requirements, SEO characteristics, performance profile, and the developer model for data fetching. Understanding the precise mechanics of each strategy (not just their marketing descriptions) is essential for making and defending the right choice for a given project.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Rendering strategy determines server infrastructure, SEO characteristics, and performance profile. SSR provides SEO and fast LCP but adds server cost and TTFB. SSG gives sub-100ms TTFB from CDN but data is stale until rebuild. ISR extends SSG with per-route revalidation. Streaming SSR and React Server Components reduce bundle size and improve perceived performance by streaming content progressively. Most real-world apps need a hybrid approach.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### SSR: Benefits and Real Costs
|
|
16
|
+
|
|
17
|
+
Server-Side Rendering generates HTML on the server for each request. The benefits are real but often overstated:
|
|
18
|
+
|
|
19
|
+
**Benefits:**
|
|
20
|
+
- **SEO**: Search engines receive pre-rendered HTML immediately. Critical for pages that must be indexed.
|
|
21
|
+
- **LCP on slow connections**: User sees content before JavaScript downloads and executes. Significant for mobile users on 3G.
|
|
22
|
+
- **Social sharing**: OG tags, Twitter Cards, and link previews work because the HTML is complete on the server response.
|
|
23
|
+
- **Auth-gated content**: Server can read session cookies and render personalized HTML without a client-side auth check causing layout flash.
|
|
24
|
+
|
|
25
|
+
**Real costs (often understated):**
|
|
26
|
+
- **TTFB**: Server must complete data fetching before sending the first byte. A slow database query delays the entire page. CSR serves a shell HTML in ~50 ms (CDN); SSR may take 200–500 ms for a database-backed page.
|
|
27
|
+
- **Server load**: Every page view consumes server CPU. At scale, this is a significant infrastructure cost vs. CDN-served static pages.
|
|
28
|
+
- **Caching complexity**: SSR responses may be personalized and cannot be cached at the CDN without careful `Vary` headers and cache key design.
|
|
29
|
+
- **Cold starts**: In serverless environments, SSR functions have cold start latency that static serving does not.
|
|
30
|
+
|
|
31
|
+
### SSG: When It Is the Right Tool
|
|
32
|
+
|
|
33
|
+
Static Site Generation pre-builds every page at deploy time. It is the correct choice for:
|
|
34
|
+
|
|
35
|
+
- Content that changes at most a few times per day (blog, docs, marketing pages)
|
|
36
|
+
- Pages with no user-specific personalization in the HTML
|
|
37
|
+
- Maximum performance requirements (sub-100 ms TTFB from CDN)
|
|
38
|
+
|
|
39
|
+
SSG limitations are real and must be understood:
|
|
40
|
+
- **Build time**: Every page generates during CI. At 10,000 pages with 100 ms per page: 17 minutes of build time. Use incremental builds and build caching to mitigate.
|
|
41
|
+
- **Data freshness**: Pages are stale from the moment they are built. A product price change requires a rebuild to appear. Use on-demand revalidation (ISR) for this case.
|
|
42
|
+
|
|
43
|
+
### ISR: Incremental Static Regeneration
|
|
44
|
+
|
|
45
|
+
ISR extends SSG with per-route revalidation intervals. A page marked `revalidate: 60` is served from the CDN static cache, but regenerated in the background every 60 seconds when a visitor triggers it:
|
|
46
|
+
|
|
47
|
+
- Stale-while-revalidate semantics: the first visitor after 60 seconds gets the old page; the next visitor gets the fresh page
|
|
48
|
+
- The regeneration happens server-side, not in response to a specific user — it is eventual consistency, not real-time
|
|
49
|
+
- On-demand ISR (via `res.revalidate(path)`) allows webhook-triggered immediate rebuilds — product price change → webhook → rebuild that product page within seconds
|
|
50
|
+
|
|
51
|
+
ISR is appropriate when "a few seconds to a minute stale" is acceptable and you want CDN-level performance without a full rebuild per data change.
|
|
52
|
+
|
|
53
|
+
### Streaming SSR
|
|
54
|
+
|
|
55
|
+
React 18 and Next.js App Router support streaming HTML responses using `renderToPipeableStream` and `<Suspense>`:
|
|
56
|
+
|
|
57
|
+
1. Server sends the page shell (header, navigation, above-fold layout) immediately — TTFB is fast
|
|
58
|
+
2. Data-dependent sections are wrapped in `<Suspense>` with a fallback (skeleton)
|
|
59
|
+
3. As each suspended section's data resolves, React streams its HTML directly into the response
|
|
60
|
+
4. Browser progressively renders content as it arrives
|
|
61
|
+
|
|
62
|
+
This transforms LCP dramatically: users see layout and fast content immediately; slow data sections (product reviews, recommendations) arrive asynchronously without blocking the critical paint.
|
|
63
|
+
|
|
64
|
+
**Implementation pattern:**
|
|
65
|
+
```tsx
|
|
66
|
+
// app/product/[id]/page.tsx
|
|
67
|
+
export default function ProductPage({ params }) {
|
|
68
|
+
return (
|
|
69
|
+
<main>
|
|
70
|
+
<ProductHero id={params.id} /> {/* Fast: renders immediately */}
|
|
71
|
+
<Suspense fallback={<ReviewSkeleton />}>
|
|
72
|
+
<ProductReviews id={params.id} /> {/* Slow: streams in when ready */}
|
|
73
|
+
</Suspense>
|
|
74
|
+
<Suspense fallback={<RecommendationSkeleton />}>
|
|
75
|
+
<Recommendations id={params.id} /> {/* Slow: streams in when ready */}
|
|
76
|
+
</Suspense>
|
|
77
|
+
</main>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### React Server Components
|
|
83
|
+
|
|
84
|
+
RSC is a model where components run exclusively on the server and send a serialized component description to the client — not HTML, not JavaScript:
|
|
85
|
+
|
|
86
|
+
- **Server Components**: Zero client-side JS. Read databases, file systems, environment variables directly. Not interactive.
|
|
87
|
+
- **Client Components**: Marked with `"use client"`. Run on both server (for initial HTML) and client (for interactivity). Receive server component output as props.
|
|
88
|
+
- **Benefit**: A product page with 40 KB of component code can ship 5 KB to the browser if most components are server-only.
|
|
89
|
+
|
|
90
|
+
The mental model shift: the client-server boundary is now drawn at the component level, not the page level. This is the most significant change in React architecture since hooks.
|
|
91
|
+
|
|
92
|
+
### Choosing a Strategy: Decision Framework
|
|
93
|
+
|
|
94
|
+
Answer these questions in order:
|
|
95
|
+
|
|
96
|
+
1. **Does this page need SEO?** If no → CSR. If yes → continue.
|
|
97
|
+
2. **Is the content personalized per user?** If no → SSG or ISR. If yes → SSR or RSC.
|
|
98
|
+
3. **How stale can the data be?** Minutes acceptable → ISR. Real-time required → SSR.
|
|
99
|
+
4. **How much traffic?** Millions of pageviews/day → CDN-served (SSG/ISR) wins on cost. Low traffic → SSR is fine.
|
|
100
|
+
|
|
101
|
+
Most real-world apps need a hybrid: SSG for marketing, ISR for product listings, SSR for checkout, CSR for the user dashboard.
|
|
102
|
+
|
|
103
|
+
### Progressive Hydration Pattern
|
|
104
|
+
|
|
105
|
+
In Next.js without RSC, implement progressive hydration by deferring non-critical component hydration:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import dynamic from "next/dynamic";
|
|
109
|
+
|
|
110
|
+
// Defer hydration until the component is in the viewport
|
|
111
|
+
const HeavyWidget = dynamic(() => import("./HeavyWidget"), {
|
|
112
|
+
ssr: false, // Not needed for SEO
|
|
113
|
+
loading: () => <WidgetSkeleton />,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Or: load but don't hydrate until user interacts
|
|
117
|
+
const ChatWidget = dynamic(() => import("./ChatWidget"), {
|
|
118
|
+
ssr: false,
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Each dynamically imported component creates a separate JS chunk. Only load what is needed for the current page — analyze your bundle with `@next/bundle-analyzer` or `rollup-plugin-visualizer`.
|
|
123
|
+
|
|
124
|
+
### Hydration Mismatch Debugging
|
|
125
|
+
|
|
126
|
+
The most common SSR debugging problem is hydration mismatches — the server renders different HTML than the client expects. Common causes:
|
|
127
|
+
|
|
128
|
+
- Browser-only APIs (`window`, `document`, `navigator`) accessed during server render
|
|
129
|
+
- Date formatting that differs between server timezone and client timezone
|
|
130
|
+
- Random values (`Math.random()`, `uuid()`) that differ between server and client renders
|
|
131
|
+
- Conditional rendering based on browser features (`typeof window !== 'undefined'`)
|
|
132
|
+
|
|
133
|
+
Fix by moving browser-only code into `useEffect` (runs only on client) or by using `dynamic(() => import(...), { ssr: false })` for components that inherently require the browser. Never suppress hydration warnings with `suppressHydrationWarning` except for timestamps and user-agent-dependent content where mismatch is expected and benign.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-app-requirements
|
|
3
|
+
description: SSR/SPA decision criteria, Core Web Vitals budgets, responsive breakpoints, browser support matrix, PWA considerations, and SEO requirements
|
|
4
|
+
topics: [web-app, requirements, performance, pwa, seo, accessibility]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Defining web app requirements before writing a line of code prevents the most expensive rework in the project lifecycle. Rendering strategy, performance budgets, and browser support targets affect every architectural decision downstream. Locking these down early — with explicit tradeoff acknowledgment — removes ambiguity and gives the team a shared definition of "done."
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Web app requirements establish rendering strategy (SSR for SEO, SPA for authenticated apps, hybrid for content-heavy sites), Core Web Vitals budgets (LCP < 2.5s, INP < 200ms, CLS < 0.1), responsive breakpoints from a shared token set, browser support matrix encoded via Browserslist, PWA scope decisions, and SEO requirements. Lock these down before sprint one.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### SSR vs SPA Decision Criteria
|
|
16
|
+
|
|
17
|
+
Rendering strategy is the first architectural decision. The wrong choice costs weeks of retrofit:
|
|
18
|
+
|
|
19
|
+
- **Choose SSR (Next.js, Remix, SvelteKit, Nuxt)** when: SEO is critical (content sites, e-commerce, marketing), first-paint performance matters on slow connections, users are on low-powered devices, or content changes frequently and needs crawlability.
|
|
20
|
+
- **Choose SPA (React, Vue, Angular with CSR)** when: the app is behind authentication (no SEO need), interactions are highly dynamic (dashboards, tools, editors), and the user base is on capable hardware with fast connections.
|
|
21
|
+
- **Choose hybrid (SSG + client hydration)** when: most pages are static but some routes need real-time data. This is the default for most content-heavy sites — build pages at deploy time, hydrate interactive islands on the client.
|
|
22
|
+
|
|
23
|
+
Never choose SSR just because it is the current trend. Server rendering adds operational complexity (cold starts, caching headers, streaming), infrastructure cost, and harder debugging. Quantify the SEO and performance benefit before committing.
|
|
24
|
+
|
|
25
|
+
### Core Web Vitals Budgets
|
|
26
|
+
|
|
27
|
+
Establish explicit targets before sprint one. These directly affect Google ranking and user conversion:
|
|
28
|
+
|
|
29
|
+
- **LCP (Largest Contentful Paint)**: Target under 2.5 seconds on a 3G mobile connection. Optimize with image preloads, font subsetting, and eliminating render-blocking resources.
|
|
30
|
+
- **FID / INP (Interaction to Next Paint)**: Target under 200 ms. Long JavaScript tasks block the main thread. Break up tasks, defer non-critical code, use Web Workers for heavy computation.
|
|
31
|
+
- **CLS (Cumulative Layout Shift)**: Target under 0.1. Reserve space for images and embeds. Avoid inserting content above the fold after initial paint.
|
|
32
|
+
|
|
33
|
+
Set budget alerts in Lighthouse CI on every PR. Treat regressions as build failures.
|
|
34
|
+
|
|
35
|
+
### Responsive Breakpoints
|
|
36
|
+
|
|
37
|
+
Establish a breakpoint set and document it in the design system. A common mobile-first set:
|
|
38
|
+
|
|
39
|
+
- **Mobile**: base styles (320–767 px)
|
|
40
|
+
- **Tablet**: 768 px and up
|
|
41
|
+
- **Desktop**: 1024 px and up
|
|
42
|
+
- **Wide**: 1280 px and up (optional; cap content width at 1440 px to prevent excessive line length)
|
|
43
|
+
|
|
44
|
+
Do not invent per-component breakpoints. All breakpoints must come from a shared token set in CSS custom properties or a design token file.
|
|
45
|
+
|
|
46
|
+
### Browser Support Matrix
|
|
47
|
+
|
|
48
|
+
Define this explicitly and commit it to the repo's `CONTRIBUTING.md` or a `BROWSERS.md` file:
|
|
49
|
+
|
|
50
|
+
- **Evergreen tier** (full support): Chrome, Edge, Firefox, Safari — last 2 major versions
|
|
51
|
+
- **Legacy tier** (functional, degraded): IE 11, Safari 12 — no graceful degradation unless business requires it
|
|
52
|
+
- **Mobile browsers**: Chrome for Android, Safari iOS — test on real devices, not just emulators
|
|
53
|
+
|
|
54
|
+
Use Browserslist to encode the matrix and share it across tools (Babel, PostCSS Autoprefixer, ESLint). Target: `"> 0.5%, last 2 versions, not dead"` as a safe default.
|
|
55
|
+
|
|
56
|
+
### PWA Considerations
|
|
57
|
+
|
|
58
|
+
Decide at project start whether PWA features are required:
|
|
59
|
+
|
|
60
|
+
- **Service worker / offline**: Required if the app must function without connectivity or if mobile install is a goal. Adds complexity — cache invalidation is the #1 source of PWA bugs.
|
|
61
|
+
- **Web App Manifest**: Low cost, high value. Enables mobile home screen install and controls display mode. Add for any app targeting mobile users.
|
|
62
|
+
- **Push notifications**: Requires backend infrastructure and user permission flows. Scope carefully — most apps do not need this.
|
|
63
|
+
|
|
64
|
+
### SEO Requirements
|
|
65
|
+
|
|
66
|
+
For public-facing apps:
|
|
67
|
+
|
|
68
|
+
- Server-render or statically generate all pages indexed by search engines. Client-rendered content is not reliably indexed.
|
|
69
|
+
- Implement `<title>`, `<meta description>`, Open Graph, and structured data (JSON-LD) on every route.
|
|
70
|
+
- Generate a sitemap at deploy time; submit to Google Search Console.
|
|
71
|
+
- Ensure canonical URLs, `robots.txt`, and proper handling of 404/301/302 responses.
|
|
72
|
+
|
|
73
|
+
### Performance Budget Enforcement
|
|
74
|
+
|
|
75
|
+
Encode budgets in `budget.json` and enforce in CI:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"resourceSizes": [
|
|
80
|
+
{ "resourceType": "script", "budget": 300 },
|
|
81
|
+
{ "resourceType": "total", "budget": 1000 },
|
|
82
|
+
{ "resourceType": "image", "budget": 500 }
|
|
83
|
+
],
|
|
84
|
+
"timings": [
|
|
85
|
+
{ "metric": "first-contentful-paint", "budget": 1500 },
|
|
86
|
+
{ "metric": "interactive", "budget": 3500 },
|
|
87
|
+
{ "metric": "largest-contentful-paint", "budget": 2500 }
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Run `lighthouse-ci` on every PR against a representative page. Block merges that regress LCP, INP, or CLS beyond tolerance.
|
|
93
|
+
|
|
94
|
+
### Defining "Supported" vs "Functional"
|
|
95
|
+
|
|
96
|
+
Do not conflate browser support tiers. Define what each means:
|
|
97
|
+
|
|
98
|
+
- **Supported**: All features work, tested in CI, regressions are P0 bugs.
|
|
99
|
+
- **Functional**: Core user journey works, advanced features may degrade gracefully.
|
|
100
|
+
- **Unsupported**: App may not load at all; display an upgrade notice.
|
|
101
|
+
|
|
102
|
+
Document which tier each browser falls into and socialize it with stakeholders before launch. Retrofitting support after launch is significantly more expensive than scoping it at project start.
|
|
103
|
+
|
|
104
|
+
### Accessibility Requirements
|
|
105
|
+
|
|
106
|
+
Accessibility is a requirement, not a feature. Decide on the compliance target upfront:
|
|
107
|
+
|
|
108
|
+
- **WCAG 2.1 AA**: Industry baseline. Required for most government and enterprise customers.
|
|
109
|
+
- **WCAG 2.2 AA**: Current standard; prefer this for new projects.
|
|
110
|
+
- **Section 508**: Required for US federal contractors.
|
|
111
|
+
|
|
112
|
+
Automated tools (axe, Lighthouse) catch ~30–40% of issues. Manual keyboard navigation and screen reader testing (VoiceOver, NVDA) must supplement automation.
|