@decocms/start 0.19.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/.cursor/skills/deco-api-call-dedup/SKILL.md +443 -0
- package/.cursor/skills/deco-apps-architecture/SKILL.md +255 -0
- package/.cursor/skills/deco-apps-architecture/app-pattern.md +288 -0
- package/.cursor/skills/deco-apps-architecture/commerce-types.md +239 -0
- package/.cursor/skills/deco-apps-architecture/new-app-guide.md +268 -0
- package/.cursor/skills/deco-apps-architecture/scripts-codegen.md +148 -0
- package/.cursor/skills/deco-apps-architecture/shared-utils.md +181 -0
- package/.cursor/skills/deco-apps-architecture/vtex-deep-structure.md +253 -0
- package/.cursor/skills/deco-apps-architecture/website-app.md +169 -0
- package/.cursor/skills/deco-apps-vtex-porting/SKILL.md +189 -0
- package/.cursor/skills/deco-apps-vtex-porting/adaptation-patterns.md +335 -0
- package/.cursor/skills/deco-apps-vtex-porting/commerce-porting.md +155 -0
- package/.cursor/skills/deco-apps-vtex-porting/cookie-auth-patterns.md +148 -0
- package/.cursor/skills/deco-apps-vtex-porting/structure-map.md +234 -0
- package/.cursor/skills/deco-apps-vtex-porting/transform-mapping.md +99 -0
- package/.cursor/skills/deco-apps-vtex-porting/website-porting.md +194 -0
- package/.cursor/skills/deco-apps-vtex-review/SKILL.md +234 -0
- package/.cursor/skills/deco-async-rendering-architecture/SKILL.md +270 -0
- package/.cursor/skills/deco-async-rendering-site-guide/SKILL.md +417 -0
- package/.cursor/skills/deco-cms-layout-caching/SKILL.md +293 -0
- package/.cursor/skills/deco-cms-route-config/SKILL.md +388 -0
- package/.cursor/skills/deco-core-architecture/SKILL.md +185 -0
- package/.cursor/skills/deco-core-architecture/blocks.md +196 -0
- package/.cursor/skills/deco-core-architecture/deco-vs-deco-start.md +191 -0
- package/.cursor/skills/deco-core-architecture/engine.md +220 -0
- package/.cursor/skills/deco-core-architecture/hooks-components.md +157 -0
- package/.cursor/skills/deco-core-architecture/plugins-clients.md +136 -0
- package/.cursor/skills/deco-core-architecture/runtime.md +116 -0
- package/.cursor/skills/deco-core-architecture/site-usage.md +165 -0
- package/.cursor/skills/deco-e2e-testing/SKILL.md +372 -0
- package/.cursor/skills/deco-e2e-testing/discovery.md +337 -0
- package/.cursor/skills/deco-e2e-testing/scripts/scaffold.sh +81 -0
- package/.cursor/skills/deco-e2e-testing/selectors.md +175 -0
- package/.cursor/skills/deco-e2e-testing/templates/package.json +18 -0
- package/.cursor/skills/deco-e2e-testing/templates/playwright.config.ts +65 -0
- package/.cursor/skills/deco-e2e-testing/templates/scripts/baseline.ts +279 -0
- package/.cursor/skills/deco-e2e-testing/templates/scripts/run-e2e.ts +194 -0
- package/.cursor/skills/deco-e2e-testing/templates/specs/ecommerce-flow.spec.ts +612 -0
- package/.cursor/skills/deco-e2e-testing/templates/tsconfig.json +12 -0
- package/.cursor/skills/deco-e2e-testing/templates/utils/metrics-collector.ts +918 -0
- package/.cursor/skills/deco-e2e-testing/troubleshooting.md +602 -0
- package/.cursor/skills/deco-edge-caching/SKILL.md +316 -0
- package/.cursor/skills/deco-full-analysis/SKILL.md +898 -0
- package/.cursor/skills/deco-full-analysis/checklists/asset-optimization.md +251 -0
- package/.cursor/skills/deco-full-analysis/checklists/bug-fix.md +189 -0
- package/.cursor/skills/deco-full-analysis/checklists/cache-strategy.md +144 -0
- package/.cursor/skills/deco-full-analysis/checklists/dependency-update.md +150 -0
- package/.cursor/skills/deco-full-analysis/checklists/hydration-fix.md +191 -0
- package/.cursor/skills/deco-full-analysis/checklists/image-optimization.md +180 -0
- package/.cursor/skills/deco-full-analysis/checklists/loader-optimization.md +165 -0
- package/.cursor/skills/deco-full-analysis/checklists/seo-fix.md +183 -0
- package/.cursor/skills/deco-full-analysis/checklists/site-cleanup.md +281 -0
- package/.cursor/skills/deco-full-analysis/discovery.md +548 -0
- package/.cursor/skills/deco-incident-debugging/SKILL.md +378 -0
- package/.cursor/skills/deco-incident-debugging/headless-mode.md +510 -0
- package/.cursor/skills/deco-incident-debugging/learnings-index.md +227 -0
- package/.cursor/skills/deco-incident-debugging/triage-workflow.md +312 -0
- package/.cursor/skills/deco-islands-migration/SKILL.md +251 -0
- package/.cursor/skills/deco-loader-n-plus-1-detector/SKILL.md +275 -0
- package/.cursor/skills/deco-performance-audit/SKILL.md +530 -0
- package/.cursor/skills/deco-performance-audit/tools-reference.md +428 -0
- package/.cursor/skills/deco-performance-audit/workflow.md +457 -0
- package/.cursor/skills/deco-server-functions-invoke/SKILL.md +92 -0
- package/.cursor/skills/deco-server-functions-invoke/architecture.md +166 -0
- package/.cursor/skills/deco-server-functions-invoke/generator.md +122 -0
- package/.cursor/skills/deco-server-functions-invoke/problem.md +98 -0
- package/.cursor/skills/deco-server-functions-invoke/troubleshooting.md +110 -0
- package/.cursor/skills/deco-site-deployment/SKILL.md +396 -0
- package/.cursor/skills/deco-site-memory-debugging/SKILL.md +121 -0
- package/.cursor/skills/deco-site-memory-debugging/cdp-connection.md +222 -0
- package/.cursor/skills/deco-site-memory-debugging/memory-analysis.md +362 -0
- package/.cursor/skills/deco-site-patterns/SKILL.md +124 -0
- package/.cursor/skills/deco-site-patterns/app-composition.md +337 -0
- package/.cursor/skills/deco-site-patterns/client-patterns.md +341 -0
- package/.cursor/skills/deco-site-patterns/cms-wiring.md +230 -0
- package/.cursor/skills/deco-site-patterns/section-patterns.md +340 -0
- package/.cursor/skills/deco-site-scaling-tuning/SKILL.md +240 -0
- package/.cursor/skills/deco-site-scaling-tuning/analysis-scripts.md +267 -0
- package/.cursor/skills/deco-start-architecture/SKILL.md +218 -0
- package/.cursor/skills/deco-start-architecture/admin-protocol.md +156 -0
- package/.cursor/skills/deco-start-architecture/cms-resolution.md +201 -0
- package/.cursor/skills/deco-start-architecture/code-quality.md +158 -0
- package/.cursor/skills/deco-start-architecture/gap-analysis.md +129 -0
- package/.cursor/skills/deco-start-architecture/sdk-utilities.md +197 -0
- package/.cursor/skills/deco-start-architecture/worker-entry-caching.md +154 -0
- package/.cursor/skills/deco-startup-analysis/SKILL.md +248 -0
- package/.cursor/skills/deco-storefront-test-checklist/SKILL.md +369 -0
- package/.cursor/skills/deco-tanstack-hydration-fixes/SKILL.md +468 -0
- package/.cursor/skills/deco-tanstack-navigation/SKILL.md +681 -0
- package/.cursor/skills/deco-tanstack-search/SKILL.md +411 -0
- package/.cursor/skills/deco-tanstack-storefront-patterns/SKILL.md +1013 -0
- package/.cursor/skills/deco-to-tanstack-migration/SKILL.md +518 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/codemod-commands.md +174 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/commerce/README.md +78 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/deco-framework/README.md +128 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/gotchas.md +719 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/imports/README.md +70 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/platform-hooks/README.md +154 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/signals/README.md +220 -0
- package/.cursor/skills/deco-to-tanstack-migration/references/vite-config/README.md +78 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/package-json.md +55 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/root-route.md +110 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/router.md +96 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/setup-ts.md +167 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/vite-config.md +122 -0
- package/.cursor/skills/deco-to-tanstack-migration/templates/worker-entry.md +67 -0
- package/.cursor/skills/deco-typescript-fixes/SKILL.md +178 -0
- package/.cursor/skills/deco-typescript-fixes/common-fixes.md +330 -0
- package/.cursor/skills/deco-typescript-fixes/strategy.md +148 -0
- package/.cursor/skills/deco-variant-selection-perf/SKILL.md +272 -0
- package/.cursor/skills/deco-vtex-fetch-cache/SKILL.md +225 -0
- package/.cursor/skills/find-skills/SKILL.md +133 -0
- package/.cursor/skills/incident-report/SKILL.md +179 -0
- package/.cursor/skills/incident-report/references/5-whys.md +75 -0
- package/.cursor/skills/incident-report/templates/client-report.md +187 -0
- package/.cursor/skills/incident-report/templates/internal-report.md +206 -0
- package/.cursor/skills/template-skill/SKILL.md +38 -0
- package/.github/workflows/release.yml +32 -0
- package/.releaserc.json +25 -0
- package/CLAUDE.md +135 -0
- package/GAP_ANALYSIS.md +224 -0
- package/GAP_ANALYSIS_V2.md +1013 -0
- package/biome.json +39 -0
- package/knip.json +5 -0
- package/package.json +87 -0
- package/scripts/generate-blocks.ts +69 -0
- package/scripts/generate-invoke.ts +378 -0
- package/scripts/generate-schema.ts +657 -0
- package/src/admin/cors.ts +29 -0
- package/src/admin/decofile.ts +72 -0
- package/src/admin/index.ts +24 -0
- package/src/admin/invoke.ts +163 -0
- package/src/admin/liveControls.ts +29 -0
- package/src/admin/meta.ts +70 -0
- package/src/admin/render.ts +205 -0
- package/src/admin/schema.ts +686 -0
- package/src/admin/setup.ts +44 -0
- package/src/cms/index.ts +59 -0
- package/src/cms/loader.ts +180 -0
- package/src/cms/registry.ts +162 -0
- package/src/cms/resolve.ts +1005 -0
- package/src/cms/sectionLoaders.ts +294 -0
- package/src/hooks/DecoPageRenderer.tsx +444 -0
- package/src/hooks/LazySection.tsx +109 -0
- package/src/hooks/LiveControls.tsx +108 -0
- package/src/hooks/SectionErrorFallback.tsx +85 -0
- package/src/hooks/index.ts +8 -0
- package/src/index.ts +5 -0
- package/src/matchers/builtins.ts +184 -0
- package/src/matchers/posthog.ts +154 -0
- package/src/middleware/decoState.ts +55 -0
- package/src/middleware/healthMetrics.ts +131 -0
- package/src/middleware/index.ts +80 -0
- package/src/middleware/liveness.ts +21 -0
- package/src/middleware/observability.ts +205 -0
- package/src/routes/adminRoutes.ts +83 -0
- package/src/routes/cmsRoute.ts +302 -0
- package/src/routes/components.tsx +34 -0
- package/src/routes/index.ts +15 -0
- package/src/sdk/analytics.ts +72 -0
- package/src/sdk/cacheHeaders.ts +268 -0
- package/src/sdk/cachedLoader.ts +206 -0
- package/src/sdk/clx.ts +3 -0
- package/src/sdk/cookie.ts +39 -0
- package/src/sdk/createInvoke.ts +57 -0
- package/src/sdk/csp.ts +59 -0
- package/src/sdk/env.ts +27 -0
- package/src/sdk/index.ts +63 -0
- package/src/sdk/instrumentedFetch.ts +137 -0
- package/src/sdk/invoke.ts +133 -0
- package/src/sdk/mergeCacheControl.ts +150 -0
- package/src/sdk/redirects.ts +217 -0
- package/src/sdk/requestContext.ts +184 -0
- package/src/sdk/serverTimings.ts +68 -0
- package/src/sdk/signal.ts +41 -0
- package/src/sdk/sitemap.ts +143 -0
- package/src/sdk/urlUtils.ts +117 -0
- package/src/sdk/useDevice.ts +82 -0
- package/src/sdk/useId.ts +7 -0
- package/src/sdk/useScript.ts +101 -0
- package/src/sdk/workerEntry.ts +703 -0
- package/src/sdk/wrapCaughtErrors.ts +107 -0
- package/src/types/index.ts +39 -0
- package/src/types/widgets.ts +13 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deco-cms-layout-caching
|
|
3
|
+
description: Cache layout sections (Header, Footer, Theme) in @decocms/start to avoid redundant CMS resolution and API calls on every navigation. Covers resolvedLayoutCache in resolve.ts, layoutInflight dedup in sectionLoaders.ts, pageInflight dedup in cmsRoute.ts, registerLayoutSections, staleTime in dev mode, and diagnosing repeated intelligent-search calls. Use when page loads trigger duplicate VTEX API calls for Header shelves, variant changes re-resolve the entire CMS page, or layout sections cause N+1 API patterns.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CMS Layout Section Caching
|
|
7
|
+
|
|
8
|
+
Multi-layer caching strategy for layout sections (Header, Footer, Theme, etc.) in `@decocms/start`. These sections appear on every page but rarely change — caching them eliminates the biggest source of redundant API calls.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
- Server logs show repeated `intelligent-search/product_search` calls for Header shelves on every navigation
|
|
13
|
+
- Variant changes trigger full CMS resolution including Header/Footer
|
|
14
|
+
- `[CMS]` logs show the same sections being resolved multiple times
|
|
15
|
+
- PDP load takes >2s and most time is spent on layout section loaders
|
|
16
|
+
- Setting up a new Deco site and want optimal caching from the start
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Architecture: 3 Caching Layers for Layout Sections
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
Request → loadCmsPage (pageInflight dedup)
|
|
24
|
+
└→ resolveDecoPage
|
|
25
|
+
├→ Layout sections → resolvedLayoutCache (5min TTL) + resolvedLayoutInflight
|
|
26
|
+
└→ Content sections → resolve normally
|
|
27
|
+
└→ runSectionLoaders
|
|
28
|
+
├→ Layout sections → layoutCache (5min TTL) + layoutInflight
|
|
29
|
+
└→ Content sections → run loader normally
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Layer | File | What it caches | TTL | Key |
|
|
33
|
+
|-------|------|----------------|-----|-----|
|
|
34
|
+
| **Page inflight** | `cmsRoute.ts` | Entire `loadCmsPage` result | In-flight only | `basePath` (no query) |
|
|
35
|
+
| **Layout resolution** | `resolve.ts` | Fully resolved CMS props for layout sections | 5 min | Block reference key |
|
|
36
|
+
| **Layout loaders** | `sectionLoaders.ts` | Section loader output for layout sections | 5 min | Component key |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Layer 1: Page In-Flight Deduplication (`cmsRoute.ts`)
|
|
41
|
+
|
|
42
|
+
Prevents concurrent `loadCmsPage` calls for the same path (e.g., prefetch + click happening simultaneously).
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
const pageInflight = new Map<string, Promise<unknown>>();
|
|
46
|
+
|
|
47
|
+
export const loadCmsPage = createServerFn({ method: "GET" }).handler(
|
|
48
|
+
async (ctx) => {
|
|
49
|
+
const fullPath = ctx.data as string;
|
|
50
|
+
const [basePath] = fullPath.split("?");
|
|
51
|
+
|
|
52
|
+
const existing = pageInflight.get(basePath);
|
|
53
|
+
if (existing) return existing;
|
|
54
|
+
|
|
55
|
+
const promise = loadCmsPageInternal(fullPath)
|
|
56
|
+
.finally(() => pageInflight.delete(basePath));
|
|
57
|
+
pageInflight.set(basePath, promise);
|
|
58
|
+
return promise;
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Why `basePath` (no query)?
|
|
64
|
+
|
|
65
|
+
The CMS page structure is the same regardless of `?skuId=X` or other query params. Using `basePath` ensures that `/product/p?skuId=1` and `/product/p?skuId=2` share the same inflight promise.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Layer 2: Layout Resolution Cache (`resolve.ts`)
|
|
70
|
+
|
|
71
|
+
Caches the fully resolved CMS output for layout sections. This is the most impactful layer because layout sections often contain embedded commerce loaders (Header with product shelves) that make expensive API calls.
|
|
72
|
+
|
|
73
|
+
### Registration
|
|
74
|
+
|
|
75
|
+
In your site's `setup.ts`:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { registerLayoutSections } from "@decocms/start/cms";
|
|
79
|
+
|
|
80
|
+
registerLayoutSections([
|
|
81
|
+
"site/sections/Header/Header.tsx",
|
|
82
|
+
"site/sections/Footer/Footer.tsx",
|
|
83
|
+
"site/sections/Theme/Theme.tsx",
|
|
84
|
+
"site/sections/Miscellaneous/CookieConsent.tsx",
|
|
85
|
+
"site/sections/Social/WhatsApp.tsx",
|
|
86
|
+
]);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### How It Works
|
|
90
|
+
|
|
91
|
+
In `resolveDecoPage`, before resolving each raw section:
|
|
92
|
+
|
|
93
|
+
1. Check if the raw block eventually resolves to a registered layout section (walks up to 5 levels of block references like `"Header - 01"` → `"Header"` → `site/sections/Header/Header.tsx`)
|
|
94
|
+
2. If layout: check `resolvedLayoutCache` → return cached result if fresh
|
|
95
|
+
3. If inflight: return existing promise (dedup concurrent resolutions)
|
|
96
|
+
4. Otherwise: resolve normally, cache result for 5 minutes
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const resolvedLayoutCache = new Map<string, { sections: ResolvedSection[]; ts: number }>();
|
|
100
|
+
const resolvedLayoutInflight = new Map<string, Promise<ResolvedSection[]>>();
|
|
101
|
+
const LAYOUT_CACHE_TTL = 5 * 60_000; // 5 minutes
|
|
102
|
+
|
|
103
|
+
// Inside resolveDecoPage:
|
|
104
|
+
const layoutKey = isRawSectionLayout(section);
|
|
105
|
+
if (layoutKey) {
|
|
106
|
+
const cached = getCachedResolvedLayout(layoutKey);
|
|
107
|
+
if (cached) return cached;
|
|
108
|
+
|
|
109
|
+
const inflight = resolvedLayoutInflight.get(layoutKey);
|
|
110
|
+
if (inflight) return inflight;
|
|
111
|
+
|
|
112
|
+
const promise = resolveRawSection(section, rctx).then((results) => {
|
|
113
|
+
setCachedResolvedLayout(layoutKey, results);
|
|
114
|
+
return results;
|
|
115
|
+
});
|
|
116
|
+
resolvedLayoutInflight.set(layoutKey, promise);
|
|
117
|
+
promise.finally(() => resolvedLayoutInflight.delete(layoutKey));
|
|
118
|
+
return promise;
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `isRawSectionLayout` — Walking Block References
|
|
123
|
+
|
|
124
|
+
CMS blocks often reference other blocks:
|
|
125
|
+
- `"Header - 01"` → resolves to `"Header"` → resolves to `{ __resolveType: "site/sections/Header/Header.tsx" }`
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
function isRawSectionLayout(section: RawSection): string | null {
|
|
129
|
+
// Walk up to 5 levels of block indirection
|
|
130
|
+
let current = section;
|
|
131
|
+
for (let depth = 0; depth < 5; depth++) {
|
|
132
|
+
const resolveType = current.__resolveType;
|
|
133
|
+
if (isLayoutSection(resolveType)) return resolveType;
|
|
134
|
+
const block = decofileData?.[resolveType];
|
|
135
|
+
if (!block || typeof block !== "object") return null;
|
|
136
|
+
current = block as RawSection;
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Layer 3: Layout Section Loader Cache (`sectionLoaders.ts`)
|
|
145
|
+
|
|
146
|
+
Caches the output of section loaders (the `export const loader` functions) for layout sections.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const layoutSections = new Set<string>();
|
|
150
|
+
const layoutCache = new Map<string, { data: ResolvedSection; ts: number }>();
|
|
151
|
+
const layoutInflight = new Map<string, Promise<ResolvedSection>>();
|
|
152
|
+
|
|
153
|
+
export async function runSectionLoaders(
|
|
154
|
+
sections: ResolvedSection[],
|
|
155
|
+
request: Request,
|
|
156
|
+
): Promise<ResolvedSection[]> {
|
|
157
|
+
return Promise.all(
|
|
158
|
+
sections.map(async (section) => {
|
|
159
|
+
const key = section.Component;
|
|
160
|
+
const loaderFn = loaderRegistry.get(key);
|
|
161
|
+
|
|
162
|
+
if (isLayoutSection(key)) {
|
|
163
|
+
// Check cache
|
|
164
|
+
const cached = layoutCache.get(key);
|
|
165
|
+
if (cached && Date.now() - cached.ts < LAYOUT_CACHE_TTL) {
|
|
166
|
+
return cached.data;
|
|
167
|
+
}
|
|
168
|
+
// Check inflight
|
|
169
|
+
const inflight = layoutInflight.get(key);
|
|
170
|
+
if (inflight) return inflight;
|
|
171
|
+
|
|
172
|
+
const promise = runLoader(section, loaderFn, request).then((result) => {
|
|
173
|
+
layoutCache.set(key, { data: result, ts: Date.now() });
|
|
174
|
+
return result;
|
|
175
|
+
});
|
|
176
|
+
layoutInflight.set(key, promise);
|
|
177
|
+
promise.finally(() => layoutInflight.delete(key));
|
|
178
|
+
return promise;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return loaderFn ? runLoader(section, loaderFn, request) : section;
|
|
182
|
+
}),
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Diagnosing Layout Cache Issues
|
|
190
|
+
|
|
191
|
+
### Symptom: Repeated `intelligent-search` calls in logs
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
[VTEX] ProductList: query="", count=100, collection="152", sort="price:desc"
|
|
195
|
+
[VTEX] ProductList: query="", count=100, collection="200", sort="price:desc"
|
|
196
|
+
[VTEX] ProductList: query="", count=20, collection="", sort="price:desc"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
These come from Header product shelves being re-resolved on every navigation.
|
|
200
|
+
|
|
201
|
+
### Fix Checklist
|
|
202
|
+
|
|
203
|
+
1. Ensure `registerLayoutSections` includes the Header section key
|
|
204
|
+
2. Verify the block reference chain resolves correctly (check `.deco/blocks/Header*.json`)
|
|
205
|
+
3. Confirm `isLayoutSection` returns `true` for the section key
|
|
206
|
+
4. Add logging to verify cache hits: `console.log("[CMS] Layout cache HIT:", layoutKey)`
|
|
207
|
+
|
|
208
|
+
### Symptom: `staleTime: 0` causes re-fetch despite `loaderDeps` filtering
|
|
209
|
+
|
|
210
|
+
In dev mode, if `routeCacheDefaults` returns `{ staleTime: 0, gcTime: 0 }`, TanStack Router always re-fetches even when `loaderDeps` returns the same deps.
|
|
211
|
+
|
|
212
|
+
**Fix**: Set minimum staleTime in dev:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// In cacheHeaders.ts → routeCacheDefaults()
|
|
216
|
+
if (isDev) return { staleTime: 5_000, gcTime: 30_000 };
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Common Errors During Implementation
|
|
222
|
+
|
|
223
|
+
### Error: `isLayoutSection is not a function`
|
|
224
|
+
|
|
225
|
+
The `isLayoutSection` function must be exported from `@decocms/start/cms`:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// cms/index.ts
|
|
229
|
+
export { isLayoutSection, registerLayoutSections } from "./sectionLoaders";
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Error: Layout sections cached but still showing stale content
|
|
233
|
+
|
|
234
|
+
The 5-minute TTL means layout sections won't reflect CMS changes for up to 5 minutes in dev. Restart the dev server to clear in-memory caches.
|
|
235
|
+
|
|
236
|
+
### Error: Block reference chain not found
|
|
237
|
+
|
|
238
|
+
If `isRawSectionLayout` returns `null` for a block like `"Header - 01"`, the block reference in `.deco/blocks/` may not resolve to the layout section. Check:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
cat '.deco/blocks/Header - 01.json' | python3 -c "import sys,json; print(json.load(sys.stdin).get('__resolveType','?'))"
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Integration with `vtexCachedFetch` SWR
|
|
247
|
+
|
|
248
|
+
Layout caching prevents re-execution of section loaders and CMS resolution for 5 minutes. But the underlying VTEX API calls also benefit from the `vtexCachedFetch` SWR cache (3 min TTL):
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
Request → Layout cache (5 min TTL)
|
|
252
|
+
└→ MISS → resolveDecoPage → section loaders
|
|
253
|
+
└→ vtexCachedFetch → fetchWithCache (3 min TTL)
|
|
254
|
+
└→ MISS → actual VTEX API call
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
This means even after the layout cache expires, the underlying API data may still be fresh in the fetch cache. The two caches work together:
|
|
258
|
+
|
|
259
|
+
| Layer | TTL | Scope |
|
|
260
|
+
|-------|-----|-------|
|
|
261
|
+
| Layout resolution cache | 5 min | Full section output (props + enrichment) |
|
|
262
|
+
| Layout section loader cache | 5 min | Section loader output only |
|
|
263
|
+
| `fetchWithCache` SWR | 3 min | Individual HTTP responses |
|
|
264
|
+
| `cachedLoader` SWR | 30-120s | Commerce loader results |
|
|
265
|
+
|
|
266
|
+
### Cart Cross-Selling on PLP — Not an Issue
|
|
267
|
+
|
|
268
|
+
Analysis confirmed that cart drawer cross-selling is **CMS-based** (products configured in admin), not API-based. The Header loader only runs `usePriceSimulationBatch` (a POST, which only runs when `userInfo` cookie exists with a CEP). On first visit without the cookie, no simulation runs at all.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Performance Impact
|
|
273
|
+
|
|
274
|
+
Before layout caching (variant change on PDP):
|
|
275
|
+
- **~30 VTEX API calls** per navigation (Header shelves × 2 resolutions)
|
|
276
|
+
- **2-3 seconds** delay
|
|
277
|
+
|
|
278
|
+
After layout caching + `vtexCachedFetch` SWR:
|
|
279
|
+
- **~8 VTEX API calls** on first load (only product-specific: PDP loader, cross-selling, simulation)
|
|
280
|
+
- **~0-2 VTEX API calls** on subsequent navigations (everything served from SWR caches)
|
|
281
|
+
- **<1 second** for cached navigations
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Related Skills
|
|
286
|
+
|
|
287
|
+
| Skill | Purpose |
|
|
288
|
+
|-------|---------|
|
|
289
|
+
| `deco-vtex-fetch-cache` | SWR fetch cache for VTEX APIs (`fetchWithCache`, `vtexCachedFetch`) |
|
|
290
|
+
| `deco-variant-selection-perf` | Eliminate server calls for same-product variant selection |
|
|
291
|
+
| `deco-api-call-dedup` | In-flight deduplication + batching for VTEX API calls |
|
|
292
|
+
| `deco-edge-caching` | Cloudflare edge caching configuration |
|
|
293
|
+
| `deco-cms-route-config` | CMS route configuration in @decocms/start |
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deco-cms-route-config
|
|
3
|
+
description: Configure CMS-driven routes in @decocms/start using cmsRouteConfig, cmsHomeRouteConfig, and admin routes. Covers the catch-all route ($.tsx), homepage route (index.tsx), admin protocol routes (meta, render, invoke), ignoreSearchParams for variant selection, staleTime/gcTime configuration, cache headers, and head/SEO setup. Use when creating a new Deco site, migrating routes from Fresh, or debugging route-level caching issues.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CMS Route Configuration in @decocms/start
|
|
7
|
+
|
|
8
|
+
Reusable route configuration factories that live in `@decocms/start/routes`. Sites use thin wrappers that delegate to these factories, keeping route files small and consistent across all Deco sites.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
- Setting up routes for a new Deco TanStack storefront
|
|
13
|
+
- Migrating Fresh routes to TanStack Start
|
|
14
|
+
- Debugging why variant changes trigger server re-fetches
|
|
15
|
+
- Configuring cache headers per page type
|
|
16
|
+
- Setting up admin protocol routes (meta, render, invoke)
|
|
17
|
+
- Understanding the relationship between `loaderDeps`, `staleTime`, and server-side caching
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Route Architecture
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Site Routes (thin wrappers) Framework (@decocms/start/routes)
|
|
25
|
+
───────────────────────── ──────────────────────────────────
|
|
26
|
+
src/routes/$.tsx ───────→ cmsRouteConfig()
|
|
27
|
+
src/routes/index.tsx ───────→ cmsHomeRouteConfig()
|
|
28
|
+
src/routes/deco/meta.ts ───────→ decoMetaRoute
|
|
29
|
+
src/routes/deco/render.ts ───────→ decoRenderRoute
|
|
30
|
+
src/routes/deco/invoke.$.ts ─────→ decoInvokeRoute
|
|
31
|
+
src/routes/__root.tsx × Site-specific (fonts, theme, CSS)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Catch-All CMS Route (`$.tsx`)
|
|
37
|
+
|
|
38
|
+
The catch-all route handles all CMS-managed pages (PDP, PLP, institutional pages, etc.).
|
|
39
|
+
|
|
40
|
+
### Site File (minimal)
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// src/routes/$.tsx
|
|
44
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
45
|
+
import { cmsRouteConfig, loadDeferredSection } from "@decocms/start/routes";
|
|
46
|
+
import { DecoPageRenderer } from "@decocms/start/hooks";
|
|
47
|
+
import type { ResolvedSection, DeferredSection } from "@decocms/start/cms";
|
|
48
|
+
import type { CacheProfile } from "@decocms/start/sdk/cacheHeaders";
|
|
49
|
+
import { cacheHeaders, routeCacheDefaults } from "@decocms/start/sdk/cacheHeaders";
|
|
50
|
+
|
|
51
|
+
const routeConfig = cmsRouteConfig({
|
|
52
|
+
siteName: "My Store",
|
|
53
|
+
defaultTitle: "My Store - Default Title",
|
|
54
|
+
ignoreSearchParams: ["skuId"],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
type PageData = {
|
|
58
|
+
resolvedSections: ResolvedSection[];
|
|
59
|
+
deferredSections: DeferredSection[];
|
|
60
|
+
cacheProfile: CacheProfile;
|
|
61
|
+
name: string;
|
|
62
|
+
path: string;
|
|
63
|
+
params: Record<string, string>;
|
|
64
|
+
} | null;
|
|
65
|
+
|
|
66
|
+
export const Route = createFileRoute("/$")({
|
|
67
|
+
...routeCacheDefaults("listing"),
|
|
68
|
+
loaderDeps: routeConfig.loaderDeps,
|
|
69
|
+
loader: routeConfig.loader as any,
|
|
70
|
+
headers: ({ loaderData }) => {
|
|
71
|
+
const data = loaderData as PageData;
|
|
72
|
+
return cacheHeaders(data?.cacheProfile ?? "listing");
|
|
73
|
+
},
|
|
74
|
+
head: ({ loaderData }) => {
|
|
75
|
+
const data = loaderData as PageData;
|
|
76
|
+
return {
|
|
77
|
+
meta: [
|
|
78
|
+
{
|
|
79
|
+
title: data?.name
|
|
80
|
+
? `${data.name} | My Store`
|
|
81
|
+
: "My Store - Default Title",
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
component: CmsPage,
|
|
87
|
+
notFoundComponent: NotFoundPage,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
function CmsPage() {
|
|
91
|
+
const data = Route.useLoaderData() as PageData;
|
|
92
|
+
const { _splat } = Route.useParams();
|
|
93
|
+
const actualPath = `/${_splat ?? ""}`;
|
|
94
|
+
|
|
95
|
+
if (!data) return <NotFoundPage />;
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<DecoPageRenderer
|
|
99
|
+
sections={data.resolvedSections ?? []}
|
|
100
|
+
deferredSections={data.deferredSections ?? []}
|
|
101
|
+
pagePath={actualPath}
|
|
102
|
+
loadDeferredSectionFn={(d) => loadDeferredSection({ data: d }) as Promise<ResolvedSection | null>}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**CRITICAL**: The `...routeCacheDefaults("listing")` spread is essential. Without it, every SPA navigation triggers a full server re-fetch even when the data was just loaded seconds ago. This is the most common cause of perceived slow navigation.
|
|
109
|
+
|
|
110
|
+
### `cmsRouteConfig` Options
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
interface CmsRouteOptions {
|
|
114
|
+
siteName: string; // Used in page title: "Page Name | siteName"
|
|
115
|
+
defaultTitle: string; // Fallback title when CMS page has no name
|
|
116
|
+
ignoreSearchParams?: string[]; // Search params excluded from loaderDeps
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `ignoreSearchParams` — Critical for Variants
|
|
121
|
+
|
|
122
|
+
`ignoreSearchParams: ["skuId"]` tells TanStack Router that `?skuId` changes should NOT trigger a loader re-fetch:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
loaderDeps: ({ search }) => {
|
|
126
|
+
const filtered = Object.fromEntries(
|
|
127
|
+
Object.entries(search ?? {}).filter(([k]) => !ignoreSet.has(k)),
|
|
128
|
+
);
|
|
129
|
+
return { search: Object.keys(filtered).length ? filtered : undefined };
|
|
130
|
+
},
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The `loader` only sees `deps.search` (which excludes `skuId`), so it builds the CMS path without `?skuId`:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
loader: async ({ params, deps }) => {
|
|
137
|
+
const basePath = "/" + (params._splat || "");
|
|
138
|
+
const searchStr = deps.search
|
|
139
|
+
? "?" + new URLSearchParams(deps.search).toString()
|
|
140
|
+
: "";
|
|
141
|
+
return loadCmsPage({ data: basePath + searchStr });
|
|
142
|
+
},
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Cache Headers — Dynamic per Page Type
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
headers: ({ loaderData }) => {
|
|
149
|
+
const profile = loaderData?.cacheProfile ?? "listing";
|
|
150
|
+
return cacheHeaders(profile);
|
|
151
|
+
},
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The `cacheProfile` is determined by `detectCacheProfile(basePath)` inside `loadCmsPage`:
|
|
155
|
+
|
|
156
|
+
| URL Pattern | Profile | Edge TTL |
|
|
157
|
+
|-------------|---------|----------|
|
|
158
|
+
| `*/p` | product | 5 min |
|
|
159
|
+
| `/s`, `?q=` | search | 60s |
|
|
160
|
+
| `/cart`, `/checkout` | private | none |
|
|
161
|
+
| Everything else | listing | 2 min |
|
|
162
|
+
|
|
163
|
+
### Head/SEO
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
head: ({ loaderData }) => ({
|
|
167
|
+
meta: [
|
|
168
|
+
{ title: loaderData?.pageName
|
|
169
|
+
? `${loaderData.pageName} | ${siteName}`
|
|
170
|
+
: defaultTitle },
|
|
171
|
+
],
|
|
172
|
+
}),
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Homepage Route (`index.tsx`)
|
|
178
|
+
|
|
179
|
+
Hardcoded to `/` path — no params, no deps.
|
|
180
|
+
|
|
181
|
+
### Site File
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// src/routes/index.tsx
|
|
185
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
186
|
+
import { cmsHomeRouteConfig, loadDeferredSection } from "@decocms/start/routes";
|
|
187
|
+
import { DecoPageRenderer } from "@decocms/start/hooks";
|
|
188
|
+
import type { ResolvedSection, DeferredSection } from "@decocms/start/cms";
|
|
189
|
+
|
|
190
|
+
const homeConfig = cmsHomeRouteConfig({
|
|
191
|
+
defaultTitle: "My Store - Homepage",
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
type HomeData = {
|
|
195
|
+
resolvedSections: ResolvedSection[];
|
|
196
|
+
deferredSections: DeferredSection[];
|
|
197
|
+
} | null;
|
|
198
|
+
|
|
199
|
+
export const Route = createFileRoute("/")({
|
|
200
|
+
...homeConfig,
|
|
201
|
+
component: HomePage,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
function HomePage() {
|
|
205
|
+
const data = Route.useLoaderData() as HomeData;
|
|
206
|
+
if (!data) return null;
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<DecoPageRenderer
|
|
210
|
+
sections={data.resolvedSections ?? []}
|
|
211
|
+
deferredSections={data.deferredSections ?? []}
|
|
212
|
+
pagePath="/"
|
|
213
|
+
loadDeferredSectionFn={(d) => loadDeferredSection({ data: d }) as Promise<ResolvedSection | null>}
|
|
214
|
+
/>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`cmsHomeRouteConfig` already includes `routeCacheDefaults("static")` and `cacheHeaders("static")`, giving the homepage a 5-min client staleTime and 24h edge TTL. Do NOT add additional cache config.
|
|
220
|
+
|
|
221
|
+
### `cmsHomeRouteConfig` Options
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
interface CmsHomeRouteOptions {
|
|
225
|
+
defaultTitle: string;
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Admin Protocol Routes
|
|
232
|
+
|
|
233
|
+
These routes enable the Deco CMS admin (admin.deco.cx) to communicate with the storefront:
|
|
234
|
+
|
|
235
|
+
### Meta Route — Schema & Manifest
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// src/routes/deco/meta.ts
|
|
239
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
240
|
+
import { decoMetaRoute } from "@decocms/start/routes";
|
|
241
|
+
|
|
242
|
+
export const Route = createFileRoute("/deco/meta")({
|
|
243
|
+
...decoMetaRoute,
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Render Route — Section Preview
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// src/routes/deco/render.ts
|
|
251
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
252
|
+
import { decoRenderRoute } from "@decocms/start/routes";
|
|
253
|
+
|
|
254
|
+
export const Route = createFileRoute("/deco/render")({
|
|
255
|
+
...decoRenderRoute,
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Invoke Route — Loader/Action Execution
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// src/routes/deco/invoke.$.ts
|
|
263
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
264
|
+
import { decoInvokeRoute } from "@decocms/start/routes";
|
|
265
|
+
|
|
266
|
+
export const Route = createFileRoute("/deco/invoke/$")({
|
|
267
|
+
...decoInvokeRoute,
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Important: Use Spread Operator
|
|
272
|
+
|
|
273
|
+
Always use `{ ...frameworkRoute }` — NOT `createFileRoute("/path")(frameworkRoute)`:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
// BAD — "Route cannot have both an 'id' and a 'path' option"
|
|
277
|
+
export const Route = createFileRoute("/deco/meta")(decoMetaRoute);
|
|
278
|
+
|
|
279
|
+
// GOOD — spread into new object
|
|
280
|
+
export const Route = createFileRoute("/deco/meta")({ ...decoMetaRoute });
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
TanStack Router injects internal properties (`id`, `path`) that conflict if the config object already has them.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Framework Exports
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// @decocms/start/routes
|
|
291
|
+
export {
|
|
292
|
+
cmsRouteConfig, // Catch-all CMS route config factory
|
|
293
|
+
cmsHomeRouteConfig, // Homepage route config factory
|
|
294
|
+
loadCmsPage, // Server function for CMS page resolution
|
|
295
|
+
loadCmsHomePage, // Server function for homepage resolution
|
|
296
|
+
type CmsRouteOptions,
|
|
297
|
+
CmsPage, // Generic CMS page component
|
|
298
|
+
NotFoundPage, // Generic 404 component
|
|
299
|
+
decoMetaRoute, // Admin meta route config
|
|
300
|
+
decoRenderRoute, // Admin render route config
|
|
301
|
+
decoInvokeRoute, // Admin invoke route config
|
|
302
|
+
};
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Add to `package.json` exports:
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"exports": {
|
|
309
|
+
"./routes": "./src/routes/index.ts"
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Common Errors
|
|
317
|
+
|
|
318
|
+
### `Cannot find module '@decocms/start/routes'`
|
|
319
|
+
|
|
320
|
+
TypeScript server needs restart after adding new exports to `package.json`. In VSCode/Cursor:
|
|
321
|
+
- Cmd+Shift+P → "TypeScript: Restart TS Server"
|
|
322
|
+
- Or restart the dev server
|
|
323
|
+
|
|
324
|
+
### `Route cannot have both an 'id' and a 'path' option`
|
|
325
|
+
|
|
326
|
+
Use spread: `{ ...decoMetaRoute }` instead of direct assignment.
|
|
327
|
+
|
|
328
|
+
### `Property 'resolvedSections' does not exist on type 'never'`
|
|
329
|
+
|
|
330
|
+
TypeScript inference limitation with `createServerFn` + `useLoaderData()`. The `page` could be `null`. Add a null check:
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
function CmsPage() {
|
|
334
|
+
const page = Route.useLoaderData();
|
|
335
|
+
if (!page) return <NotFoundPage />;
|
|
336
|
+
return <DecoPageRenderer sections={page.resolvedSections} />;
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Root Route (`__root.tsx`) — Keep Site-Specific
|
|
341
|
+
|
|
342
|
+
The root route contains site-specific elements that should NOT be in the framework:
|
|
343
|
+
- HTML lang attribute
|
|
344
|
+
- Favicon
|
|
345
|
+
- CSS stylesheet imports
|
|
346
|
+
- Font loading
|
|
347
|
+
- Theme configuration
|
|
348
|
+
- QueryClient setup
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## `staleTime` / `gcTime` Configuration
|
|
353
|
+
|
|
354
|
+
### Production
|
|
355
|
+
|
|
356
|
+
Set by `routeCacheDefaults(profile)` based on page type (from `cacheHeaders.ts`):
|
|
357
|
+
|
|
358
|
+
| Profile | staleTime | gcTime |
|
|
359
|
+
|---------|-----------|--------|
|
|
360
|
+
| static | 5 min | 30 min |
|
|
361
|
+
| product | 1 min | 5 min |
|
|
362
|
+
| listing | 1 min | 5 min |
|
|
363
|
+
| search | 30s | 2 min |
|
|
364
|
+
| cart | 0 | 0 |
|
|
365
|
+
| private | 0 | 0 |
|
|
366
|
+
| none | 0 | 0 |
|
|
367
|
+
|
|
368
|
+
### Development
|
|
369
|
+
|
|
370
|
+
`staleTime: 5_000` (5 seconds) — not zero!
|
|
371
|
+
|
|
372
|
+
With `staleTime: 0`, TanStack Router re-fetches on every navigation even if `loaderDeps` returns identical deps. This causes:
|
|
373
|
+
- Double-fetch on variant changes (despite `ignoreSearchParams`)
|
|
374
|
+
- Prefetch + click = 2 server calls
|
|
375
|
+
|
|
376
|
+
Setting 5s staleTime allows rapid interactions (variant clicks, back/forward) to use cached data while still reflecting changes within a few seconds.
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Related Skills
|
|
381
|
+
|
|
382
|
+
| Skill | Purpose |
|
|
383
|
+
|-------|---------|
|
|
384
|
+
| `deco-variant-selection-perf` | Variant selection optimization using replaceState |
|
|
385
|
+
| `deco-cms-layout-caching` | Layout section caching in CMS resolve |
|
|
386
|
+
| `deco-edge-caching` | Cloudflare edge caching with workerEntry |
|
|
387
|
+
| `deco-tanstack-storefront-patterns` | General storefront patterns |
|
|
388
|
+
| `deco-start-architecture` | Full @decocms/start architecture reference |
|