create-content-sdk-app 2.2.0-canary.20260625092313 → 2.2.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/dist/templates/nextjs-app-router/.agents/skills/content-sdk-graphql-data-fetching/SKILL.md +3 -3
- package/dist/templates/nextjs-app-router/.agents/skills/content-sdk-multisite-management/SKILL.md +1 -1
- package/dist/templates/nextjs-app-router/.windsurfrules +1 -0
- package/dist/templates/nextjs-app-router/AGENTS.md +2 -2
- package/dist/templates/nextjs-app-router/Skills.md +2 -2
- package/dist/templates/nextjs-app-router/copilot-instructions.md +1 -0
- package/dist/templates/nextjs-app-router/src/app/[site]/[locale]/[[...path]]/page.tsx +1 -9
- package/dist/templates/nextjs-app-router-cache-components/.agents/skills/content-sdk-cache-components-and-osr/SKILL.md +2 -1
- package/dist/templates/nextjs-app-router-cache-components/.agents/skills/content-sdk-graphql-data-fetching/SKILL.md +5 -4
- package/dist/templates/nextjs-app-router-cache-components/.agents/skills/content-sdk-multisite-management/SKILL.md +1 -1
- package/dist/templates/nextjs-app-router-cache-components/.agents/skills/content-sdk-route-configuration/SKILL.md +2 -2
- package/dist/templates/nextjs-app-router-cache-components/.agents/skills/content-sdk-site-setup-and-env/SKILL.md +1 -1
- package/dist/templates/nextjs-app-router-cache-components/.cursor/rules/sitecore.mdc +3 -2
- package/dist/templates/nextjs-app-router-cache-components/.windsurfrules +2 -1
- package/dist/templates/nextjs-app-router-cache-components/AGENTS.md +11 -9
- package/dist/templates/nextjs-app-router-cache-components/Skills.md +3 -3
- package/dist/templates/nextjs-app-router-cache-components/copilot-instructions.md +2 -1
- package/dist/templates/nextjs-app-router-cache-components/src/app/[site]/[locale]/[[...path]]/not-found.tsx +15 -8
- package/dist/templates/nextjs-app-router-cache-components/src/app/[site]/[locale]/[[...path]]/page.tsx +14 -3
- package/dist/templates/nextjs-app-router-cache-components/src/lib/sitecore-build-validation.ts +14 -0
- package/package.json +6 -6
package/dist/templates/nextjs-app-router/.agents/skills/content-sdk-graphql-data-fetching/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: content-sdk-graphql-data-fetching
|
|
3
|
-
description: Fetches page data and dictionary via the single Sitecore client. App Router: getPage(path ?? [], { site, locale }), getDictionary, getAppRouterStaticParams; for preview use draftMode() and getPreview/getDesignLibraryData from searchParams. Use when fetching page or dictionary content.
|
|
3
|
+
description: Fetches page data and dictionary via the single Sitecore client. App Router: getPage(path ?? [], { site, locale }), getDictionary, getAppRouterStaticParams in generateStaticParams when generateStaticPaths is enabled; for preview use draftMode() and getPreview/getDesignLibraryData from searchParams. Use when fetching page or dictionary content.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Content SDK GraphQL Data Fetching (App Router)
|
|
@@ -15,7 +15,7 @@ All Sitecore data fetching goes through the single client in `src/lib/sitecore-c
|
|
|
15
15
|
|
|
16
16
|
## How to perform
|
|
17
17
|
|
|
18
|
-
- Use the client from `src/lib/sitecore-client.ts` only. In the catch-all page: `await params`, then `client.getPage(path ?? [], { site, locale })`. For SSG
|
|
18
|
+
- Use the client from `src/lib/sitecore-client.ts` only. In the catch-all page: `await params`, then `client.getPage(path ?? [], { site, locale })`. For SSG, follow the `generateStaticParams` rules in Hard Rules below. For preview use `draftMode()` and `getPreview`/`getDesignLibraryData` from searchParams.
|
|
19
19
|
|
|
20
20
|
## Hard Rules
|
|
21
21
|
|
|
@@ -23,7 +23,7 @@ All Sitecore data fetching goes through the single client in `src/lib/sitecore-c
|
|
|
23
23
|
- Pass **site** and **locale** from route params (e.g. `await params` in the page). Do not rely on global state for site/locale in server code.
|
|
24
24
|
- **Catch-all page:** `client.getPage(path ?? [], { site, locale })`. Params are a Promise (Next.js 15+); use `await params` to get `{ site, locale, path? }`.
|
|
25
25
|
- **Preview:** Use `draftMode()`; if `draft.isEnabled`, use `client.getPreview(editingParams)` or `client.getDesignLibraryData(editingParams)` from **searchParams**. Otherwise use getPage with site and locale.
|
|
26
|
-
- **SSG:** `generateStaticParams
|
|
26
|
+
- **SSG:** In `generateStaticParams`, call `client.getAppRouterStaticParams(siteNames, locales)` where site names come from `.sitecore/sites.json` (e.g. `sites.map((s) => s.name)`) and locales from `src/i18n/routing.ts` (e.g. `routing.locales.slice()`), but only when `process.env.NODE_ENV !== 'development'` and `scConfig.generateStaticPaths` is true. Otherwise return `[]`. Do not synthesize a fallback param (e.g. `{ site: 'default', locale, path: [] }`).
|
|
27
27
|
- Config for the client comes from `sitecore.config.ts`; use environment variables, never hardcode secrets.
|
|
28
28
|
|
|
29
29
|
## Stop Conditions
|
package/dist/templates/nextjs-app-router/.agents/skills/content-sdk-multisite-management/SKILL.md
CHANGED
|
@@ -19,7 +19,7 @@ Site resolution from request (e.g. hostname or cookie); proxy rewrites to /[site
|
|
|
19
19
|
|
|
20
20
|
## Hard Rules
|
|
21
21
|
|
|
22
|
-
- Site list is in `.sitecore/sites.json` (
|
|
22
|
+
- Site list is in `.sitecore/sites.json` (generated by CLI `generateSites`). When multisite is enabled, sites come from Edge; the configured default site is prepended **only** when `scConfig.defaultSite` is explicitly set (`NEXT_PUBLIC_DEFAULT_SITE_NAME`). Do not synthesize a fake site when env is unset. Avoid hand-editing unless the format is known.
|
|
23
23
|
- **Proxy:** Implemented in `src/proxy.ts`. Next.js runs middleware from `middleware.ts` at root or in `src/` — if the app only has `proxy.ts`, add `src/middleware.ts` that re-exports it.
|
|
24
24
|
- **Chain order is fixed:** LocaleProxy → AppRouterMultisiteProxy → RedirectsProxy → PersonalizeProxy. **Do not change this order.** LocaleProxy runs first so i18n and multisite see the correct locale. AppRouterMultisiteProxy rewrites to /[site]/[locale]/[...path].
|
|
25
25
|
- **Matcher:** Exclude API routes, _next/, sitemap, robots, healthz, Sitecore paths, and static assets so the proxy stays lightweight.
|
|
@@ -47,6 +47,7 @@ src/
|
|
|
47
47
|
### @sitecore-content-sdk
|
|
48
48
|
|
|
49
49
|
- Use `SitecoreClient` for content fetching
|
|
50
|
+
- In `generateStaticParams`, call `getAppRouterStaticParams` only when `scConfig.generateStaticPaths` is true; otherwise return `[]`
|
|
50
51
|
- Implement proper error handling with try/catch blocks
|
|
51
52
|
- Cache API responses using React Query or SWR
|
|
52
53
|
- Handle content preview vs. published content scenarios
|
|
@@ -104,7 +104,7 @@ These are the main head-app–specific concepts. Details are in the sections bel
|
|
|
104
104
|
|
|
105
105
|
### Multisite and Edge middleware (proxy)
|
|
106
106
|
|
|
107
|
-
- **Site list:** `.sitecore/sites.json` —
|
|
107
|
+
- **Site list:** `.sitecore/sites.json` — generated by the Sitecore CLI (`generateSites`). Contains Edge sites when multisite is enabled; prepends the configured default site **only** when `scConfig.defaultSite` is explicitly set (`NEXT_PUBLIC_DEFAULT_SITE_NAME`). Do not synthesize a fake site when env is unset. Used by middleware and API route handlers. Avoid hand-editing unless you know the format.
|
|
108
108
|
- **Edge middleware:** Implemented in **`src/proxy.ts`**. Next.js only runs middleware from a file named `middleware.ts` at root or in `src/`. If this app has only `proxy.ts`, add `src/middleware.ts` that re-exports it (e.g. `export { default } from './proxy';`) so the proxy runs.
|
|
109
109
|
- **Proxy chain (order is critical):** `defineProxy(locale, multisite, redirects, personalize).exec(req)`:
|
|
110
110
|
- **LocaleProxy** — runs first; uses `sites` and `routing.locales` from `src/i18n/routing.ts`. Required for App Router so locale is set before multisite.
|
|
@@ -117,7 +117,7 @@ These are the main head-app–specific concepts. Details are in the sections bel
|
|
|
117
117
|
### Data fetching and preview
|
|
118
118
|
|
|
119
119
|
- **Page data:** In the page (or a Server Component), use `client.getPage(path ?? [], { site, locale })`. For preview, use `draftMode()`; if `draft.isEnabled`, use `client.getPreview(editingParams)` or `client.getDesignLibraryData(editingParams)` from `searchParams`; otherwise use `getPage` with `site` and `locale`.
|
|
120
|
-
- **SSG:** `generateStaticParams
|
|
120
|
+
- **SSG:** In `generateStaticParams`, call `client.getAppRouterStaticParams(sites, routing.locales)` (sites from `.sitecore/sites.json`) only when `process.env.NODE_ENV !== 'development'` and `scConfig.generateStaticPaths` is true. Otherwise return `[]` (local dev, editing hosts, or `GENERATE_STATIC_PATHS=false`). Do not synthesize a fallback param (e.g. `{ site: 'default', locale, path: [] }`).
|
|
121
121
|
- **Metadata:** `generateMetadata` in the same segment can call `client.getPage(path ?? [], { site, locale })` and derive `title` (e.g. from route fields). Next.js will cache as appropriate.
|
|
122
122
|
|
|
123
123
|
### Server vs Client components
|
|
@@ -32,7 +32,7 @@ Using SDK field components: `<Text>`, `<RichText>`, `<Image>`, `<Link>`, with pr
|
|
|
32
32
|
|
|
33
33
|
### content-sdk-graphql-data-fetching
|
|
34
34
|
|
|
35
|
-
Page and dictionary fetching via the single Sitecore client in `src/lib/sitecore-client.ts`. Use `getPage(path ?? [], { site, locale })`, `getDictionary`, `getAppRouterStaticParams`
|
|
35
|
+
Page and dictionary fetching via the single Sitecore client in `src/lib/sitecore-client.ts`. Use `getPage(path ?? [], { site, locale })`, `getDictionary`, and `client.getAppRouterStaticParams` in `generateStaticParams` when `scConfig.generateStaticPaths` is true (otherwise return `[]`). For preview use `draftMode()` and `getPreview`/`getDesignLibraryData` from searchParams.
|
|
36
36
|
|
|
37
37
|
### content-sdk-route-configuration
|
|
38
38
|
|
|
@@ -44,7 +44,7 @@ Site and environment: `sitecore.config.ts`, environment variables, default site
|
|
|
44
44
|
|
|
45
45
|
### content-sdk-multisite-management
|
|
46
46
|
|
|
47
|
-
Multisite: `.sitecore/sites.json
|
|
47
|
+
Multisite: `.sitecore/sites.json` (CLI `generateSites` — Edge sites plus configured `defaultSite` only when `NEXT_PUBLIC_DEFAULT_SITE_NAME` is set), proxy in `src/proxy.ts`. Chain order is **fixed:** LocaleProxy → AppRouterMultisiteProxy → RedirectsProxy → PersonalizeProxy. Do not change proxy order.
|
|
48
48
|
|
|
49
49
|
### content-sdk-dictionary-and-i18n
|
|
50
50
|
|
|
@@ -42,6 +42,7 @@ src/
|
|
|
42
42
|
|
|
43
43
|
### @sitecore-content-sdk
|
|
44
44
|
- Use `SitecoreClient` for content fetching
|
|
45
|
+
- In `generateStaticParams`, call `getAppRouterStaticParams` only when `scConfig.generateStaticPaths` is true; otherwise return `[]`
|
|
45
46
|
- Implement proper error handling with try/catch blocks
|
|
46
47
|
- Cache API responses using React Query or SWR
|
|
47
48
|
- Handle content preview vs. published content scenarios
|
|
@@ -64,15 +64,7 @@ export const generateStaticParams = async () => {
|
|
|
64
64
|
routing.locales.slice()
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
|
-
|
|
68
|
-
// Return a default param for the root page
|
|
69
|
-
return [
|
|
70
|
-
{
|
|
71
|
-
site: sites[0]?.name || 'default',
|
|
72
|
-
locale: routing.defaultLocale || scConfig.defaultLanguage,
|
|
73
|
-
path: [],
|
|
74
|
-
},
|
|
75
|
-
];
|
|
67
|
+
return [];
|
|
76
68
|
};
|
|
77
69
|
<% } -%>
|
|
78
70
|
// Metadata fields for the page.
|
|
@@ -11,7 +11,7 @@ This template is the cache-aware variant of the App Router template. It enables
|
|
|
11
11
|
- `getSitecorePage({ site, locale, path })` → `sc:route:...`, `sc:item:...` (variants are isolated naturally by the URL path / Cache Components key, no `sc:pvv:` tag is added)
|
|
12
12
|
- `getSitecoreDictionary({ site, locale })` → `sc:dict:{site}:{locale}`
|
|
13
13
|
- `getSitecoreErrorPage({ site, locale, code })` → same tag strategy as `getSitecorePage`
|
|
14
|
-
- A **single Sitecore-webhook endpoint** at `POST /api/revalidate` (in `src/app/api/revalidate/route.ts`) built with `createSitecoreRevalidateRouteHandler`. It consumes the Experience Edge / Content Operations payload shape — `updates[]` are translated into `sc:item:<id>:<locale>:latest` tags, and `tags[]` is a pass-through array that accepts `sc:`-prefixed strings verbatim (handy for ad-hoc / operational calls) or bare item IDs (mapped to `sc:item:<id>:<defaultLocale>:latest`). Dictionary tags from `sites` (`.sitecore/sites.json
|
|
14
|
+
- A **single Sitecore-webhook endpoint** at `POST /api/revalidate` (in `src/app/api/revalidate/route.ts`) built with `createSitecoreRevalidateRouteHandler`. It consumes the Experience Edge / Content Operations payload shape — `updates[]` are translated into `sc:item:<id>:<locale>:latest` tags, and `tags[]` is a pass-through array that accepts `sc:`-prefixed strings verbatim (handy for ad-hoc / operational calls) or bare item IDs (mapped to `sc:item:<id>:<defaultLocale>:latest`). Dictionary tags from `sites` (`.sitecore/sites.json`; configured `defaultSite` from `generateSites` only when `NEXT_PUBLIC_DEFAULT_SITE_NAME` is set) are appended on every call.
|
|
15
15
|
- Optional auth via **`SITECORE_REVALIDATE_SECRET`**: when set, send the same value in **`x-revalidate-secret`**; when empty, no header is required.
|
|
16
16
|
- A `sitecore.config.ts` with the **SDK in-process dictionary cache disabled** (`dictionary: { caching: { enabled: false } }`) so dictionary updates flow through Cache Components only.
|
|
17
17
|
|
|
@@ -44,6 +44,7 @@ This template is the cache-aware variant of the App Router template. It enables
|
|
|
44
44
|
- Do **not** call `revalidateTag` directly from components; route all invalidation through `/api/revalidate` (or call the route handler in tests).
|
|
45
45
|
- **Adding tags to a new helper:** Mirror the existing helpers. Compute tags with SDK helpers when applicable, fall back to deterministic strings (`sc:something:{site}:{locale}`) otherwise, and ensure those tags are also producible from whatever event triggers invalidation (webhook `updates[]` or ad-hoc `tags[]`).
|
|
46
46
|
- **Sitemap / robots / editing routes** with `cacheComponents: true` do not need an explicit `export const dynamic = 'force-dynamic'` — Next.js infers it.
|
|
47
|
+
- **Editing-host build (`GENERATE_STATIC_PATHS=false`):** Cache Components forbids `return []` from `generateStaticParams`. Return one param `{ site: BUILD_VALIDATION_SITE, locale, path: [] }` from `src/lib/sitecore-build-validation.ts` (`_DEFAULT_` — build-only, not a real Sitecore site). In `page.tsx` / `generateMetadata`, call `isBuildValidationSite(site)` and skip Edge (`setCachedPageParams`, then `notFound()` or minimal metadata). On XM Cloud, `SITECORE` is set during the **build** so `generateSites` skips Edge and `.sitecore/sites.json` can be `[]`; do not put `SITECORE` in `.env.local` for normal `npm start` (it enables editing-host `PreviewProxy` behavior). Locally, simulate with `SITECORE=true npm run build`. Empty `NEXT_PUBLIC_DEFAULT_SITE_NAME` does not block `/` when `sites.json` has Edge sites — routing uses `sites.json`, not only the env var.
|
|
47
48
|
|
|
48
49
|
## Debugging stale content
|
|
49
50
|
|
|
@@ -20,15 +20,16 @@ This template ships **tag-aware cache helpers** under `src/lib/cache/`. All non-
|
|
|
20
20
|
- Dictionary: `getSitecoreDictionary({ site, locale })`
|
|
21
21
|
- 404 / 500 Sitecore error content (Server context): `getSitecoreErrorPage({ site, locale, code })`
|
|
22
22
|
- For preview / draft, import the SDK client from `src/lib/sitecore-client.ts` and call `client.getPreview(editingParams)` or `client.getDesignLibraryData(editingParams)` directly. Do **not** wrap these in `'use cache'`.
|
|
23
|
-
- For SSG params
|
|
23
|
+
- For SSG params in `generateStaticParams`, call `client.getAppRouterStaticParams(siteNames, locales)` only when `process.env.NODE_ENV !== 'development'` and `scConfig.generateStaticPaths` is true. When `generateStaticPaths` is false, return one param from `BUILD_VALIDATION_SITE` in `src/lib/sitecore-build-validation.ts` (Cache Components forbids `return []`).
|
|
24
24
|
|
|
25
25
|
## Hard Rules
|
|
26
26
|
|
|
27
27
|
- **Cached reads (non-preview):** Use the helpers in `src/lib/cache/`. Do not call `client.getPage` / `client.getDictionary` / `client.getErrorPage` directly in pages, layouts, or i18n config — bypassing the helpers means the read is missing Sitecore cache tags and `revalidateTag` will not invalidate it.
|
|
28
28
|
- **Preview reads:** Use `client.getPreview(editingParams)` / `client.getDesignLibraryData(editingParams)` **directly**. Preview must stay dynamic; do not put it under `'use cache'`.
|
|
29
|
-
- **Catch-all page (`src/app/[site]/[locale]/[[...path]]/page.tsx`):** `await params` → check `draftMode().isEnabled`; if enabled, use the client directly with `searchParams`;
|
|
30
|
-
-
|
|
31
|
-
-
|
|
29
|
+
- **Catch-all page (`src/app/[site]/[locale]/[[...path]]/page.tsx`):** `await params` → if `isBuildValidationSite(site)` (`BUILD_VALIDATION_SITE` / `_DEFAULT_`), call `setCachedPageParams({ site, locale })` then `notFound()` without Edge. Otherwise check `draftMode().isEnabled`; if enabled, use the client directly with `searchParams`; else call `getSitecorePage({ site, locale, path: path ?? [] })`. Call `setCachedPageParams` before every `notFound()` on real routes.
|
|
30
|
+
- **Segment not-found:** `getCachedPageParams()` → static HTML when site is empty or `isBuildValidationSite(site)`; otherwise `getSitecoreErrorPage`. Do not call `headers()` in not-found.
|
|
31
|
+
- **`generateMetadata`** in the same segment should call `getSitecorePage` for real sites; return `{ title: 'Page' }` when `isBuildValidationSite(site)`.
|
|
32
|
+
- **SSG:** In `generateStaticParams`, call `client.getAppRouterStaticParams(siteNames, locales)` where site names come from `.sitecore/sites.json` (e.g. `sites.map((s) => s.name)`) and locales from `src/i18n/routing.ts` (e.g. `routing.locales.slice()`), but only when `process.env.NODE_ENV !== 'development'` and `scConfig.generateStaticPaths` is true. When `generateStaticPaths` is false, return `{ site: BUILD_VALIDATION_SITE, locale, path: [] }` from `src/lib/sitecore-build-validation.ts` — not `return []`, not `sites[0]`, not hardcoded `'default'`. See [empty-generate-static-params](https://nextjs.org/docs/messages/empty-generate-static-params).
|
|
32
33
|
- **Single SitecoreClient instance** in `src/lib/sitecore-client.ts`. The cache helpers import this client; do not create a second client.
|
|
33
34
|
- Pass **site** and **locale** from route params (e.g. `await params` in the page). Do not rely on global state for site/locale in server code.
|
|
34
35
|
- Config for the client comes from `sitecore.config.ts`; use environment variables, never hardcode secrets.
|
|
@@ -19,7 +19,7 @@ Site resolution from request (e.g. hostname or cookie); proxy rewrites to /[site
|
|
|
19
19
|
|
|
20
20
|
## Hard Rules
|
|
21
21
|
|
|
22
|
-
- Site list is in `.sitecore/sites.json` (
|
|
22
|
+
- Site list is in `.sitecore/sites.json` (generated by CLI `generateSites` in `sitecore.cli.config.ts`). When multisite is enabled, sites come from Edge; the configured default site is prepended **only** when `scConfig.defaultSite` is explicitly set (`NEXT_PUBLIC_DEFAULT_SITE_NAME`). Do not synthesize a fake site when env is unset. Avoid hand-editing unless the format is known.
|
|
23
23
|
- **Proxy:** Implemented in `src/proxy.ts`. Next.js runs middleware from `middleware.ts` at root or in `src/` — if the app only has `proxy.ts`, add `src/middleware.ts` that re-exports it.
|
|
24
24
|
- **Chain order is fixed:** PreviewProxy → BotTrackingProxy → LocaleProxy → AppRouterMultisiteProxy → RedirectsProxy → PersonalizeProxy. **Do not change this order.** PreviewProxy authorizes preview first; LocaleProxy must run before multisite so i18n and multisite see the correct locale. AppRouterMultisiteProxy rewrites to /[site]/[locale]/[...path].
|
|
25
25
|
- **Matcher:** Exclude API routes (including `/api/revalidate`), `_next/`, sitemap, robots, healthz, Sitecore paths, and static assets so the proxy stays lightweight.
|
|
@@ -15,7 +15,7 @@ Single catch-all route and layout hierarchy. Site and locale are **in the path**
|
|
|
15
15
|
|
|
16
16
|
## How to perform
|
|
17
17
|
|
|
18
|
-
- Single Sitecore page: `src/app/[site]/[locale]/[[...path]]/page.tsx`. Use `await params` for `{ site, locale, path? }`; if `draftMode().isEnabled` use the client directly
|
|
18
|
+
- Single Sitecore page: `src/app/[site]/[locale]/[[...path]]/page.tsx`. Use `await params` for `{ site, locale, path? }`; if `isBuildValidationSite(site)` skip Edge (`setCachedPageParams`, `notFound()`); else if `draftMode().isEnabled` use the client directly; otherwise call `getSitecorePage({ site, locale, path: path ?? [] })`. Call `setCachedPageParams` before each `notFound()` on real routes. Layout chain: `app/layout.tsx` → `app/[site]/layout.tsx` (Bootstrap, draftMode) → `app/[site]/[locale]/[[...path]]/layout.tsx` (calls `setCachedPageParams({ site, locale })`) → page. Segment not-found: reads `getCachedPageParams()`; static HTML when site is empty or `isBuildValidationSite(site)`; else `getSitecoreErrorPage`. Root not-found: `src/app/not-found.tsx` uses `scConfig.defaultSite` / `scConfig.defaultLanguage`.
|
|
19
19
|
|
|
20
20
|
## Hard Rules
|
|
21
21
|
|
|
@@ -26,7 +26,7 @@ Single catch-all route and layout hierarchy. Site and locale are **in the path**
|
|
|
26
26
|
- **`generateMetadata`** in the same segment should mirror the page's `draftMode` branching (preview → `client.getPreview` / `client.getDesignLibraryData`; otherwise `getSitecorePage`) so the `<title>` matches the rendered body and shares the same cache entry in the non-draft path.
|
|
27
27
|
- **Layout hierarchy:** `app/layout.tsx` → `app/[site]/layout.tsx` (Bootstrap with `siteName={site}` and `draftMode()`) → `app/[site]/[locale]/[[...path]]/layout.tsx` (calls `setCachedPageParams({ site, locale })`) → page. Do not put site/locale-specific data fetching in the root layout. **Keep the segment layout's `setCachedPageParams` call** — the segment `not-found.tsx` depends on it.
|
|
28
28
|
- Placeholders are rendered by the layout (e.g. Placeholder component); do not change placeholder names or structure without aligning with Sitecore layout definition.
|
|
29
|
-
- **Segment not-found (404):** `src/app/[site]/[locale]/[[...path]]/not-found.tsx`. Reads `{ site, locale }` via `getCachedPageParams()` (set by the segment layout
|
|
29
|
+
- **Segment not-found (404):** `src/app/[site]/[locale]/[[...path]]/not-found.tsx`. Reads `{ site, locale }` via `getCachedPageParams()` (set by the segment layout and by the page before `notFound()`), with `scConfig.defaultSite` / `scConfig.defaultLanguage` as fallback. If `!resolvedSite?.trim()` or `isBuildValidationSite(resolvedSite)` (`_DEFAULT_`), return static fallback HTML without Edge. Otherwise call `getSitecoreErrorPage({ site, locale, code: ErrorPage.NotFound })`. Wrap Sitecore 404 layout in `NextIntlClientProvider`. **Do not** call `headers()` here.
|
|
30
30
|
- **Root not-found (fallback 404):** `src/app/not-found.tsx`. Used when no segment claims the request (e.g. an unknown top-level path). Resolves site/locale from `scConfig.defaultSite` / `scConfig.defaultLanguage` and calls `getSitecoreErrorPage(...)` so 404 content gets the same Sitecore cache tags (`sc:route`, `sc:item`) as a normal page and can be invalidated by webhook.
|
|
31
31
|
- **Server error (500):** `src/app/global-error.tsx` is a Client Component (`'use client'`) and calls `client.getErrorPage(ErrorPage.InternalServerError, …)` from the client side. It is **not** cached — the cache helpers are server-only.
|
|
32
32
|
|
|
@@ -21,7 +21,7 @@ Central config in sitecore.config.ts; all secrets and environment-specific value
|
|
|
21
21
|
|
|
22
22
|
- Use `sitecore.config.ts` with `defineConfig` from the SDK. Expose api (edge, local), defaultSite, defaultLanguage, editingSecret, multisite, redirects, personalize as needed.
|
|
23
23
|
- All sensitive or environment-specific values must come from environment variables (e.g. `process.env.SITECORE_API_KEY`, `process.env.SITECORE_REVALIDATE_SECRET`). Never hardcode API keys, secrets, or production URLs in source.
|
|
24
|
-
- **Document in `.env.*.example`:** `SITECORE_EDGE_CONTEXT_ID`, `NEXT_PUBLIC_SITECORE_EDGE_CONTEXT_ID`, `SITECORE_API_KEY` / `SITECORE_API_HOST` (local), `NEXT_PUBLIC_DEFAULT_SITE_NAME`, `NEXT_PUBLIC_DEFAULT_LANGUAGE`, `SITECORE_EDITING_SECRET`, `SITECORE_REVALIDATE_SECRET` (optional; empty = no `x-revalidate-secret` header).
|
|
24
|
+
- **Document in `.env.*.example`:** `SITECORE_EDGE_CONTEXT_ID`, `NEXT_PUBLIC_SITECORE_EDGE_CONTEXT_ID`, `SITECORE_API_KEY` / `SITECORE_API_HOST` (local), `NEXT_PUBLIC_DEFAULT_SITE_NAME`, `NEXT_PUBLIC_DEFAULT_LANGUAGE`, `SITECORE_EDITING_SECRET`, `SITECORE_REVALIDATE_SECRET` (optional; empty = no `x-revalidate-secret` header), `GENERATE_STATIC_PATHS` (optional; `false` for editing-host builds — OSR still uses `BUILD_VALIDATION_SITE` in `generateStaticParams`, not `return []`).
|
|
25
25
|
- **Dictionary cache must stay disabled** in `sitecore.config.ts`: `dictionary: { caching: { enabled: false } }`. The cache helper in `src/lib/cache/get-sitecore-dictionary.ts` is the only dictionary cache layer; if you re-enable the SDK cache, `revalidateTag` will not invalidate dictionary data.
|
|
26
26
|
- Document every new or changed env var in `.env.example` (or `.env.remote.example` / `.env.container.example`). Use placeholder or empty value and a short comment; never put real secrets in example files.
|
|
27
27
|
- Never commit `.env` or `.env.local`; they are gitignored. Example files are the source of truth for which vars exist.
|
|
@@ -103,7 +103,8 @@ Dynamic Routes:
|
|
|
103
103
|
|
|
104
104
|
- Use [[...path]] for Sitecore catch-all routes (under `[site]/[locale]/`)
|
|
105
105
|
- Handle route parameters appropriately
|
|
106
|
-
- Implement proper 404 handling
|
|
106
|
+
- Implement proper 404 handling: segment `src/app/[site]/[locale]/[[...path]]/not-found.tsx` (reads `getCachedPageParams()`; static HTML when site is empty or `isBuildValidationSite(site)`; otherwise `getSitecoreErrorPage`) and root `src/app/not-found.tsx` (falls back to `scConfig.defaultSite` / `scConfig.defaultLanguage`)
|
|
107
|
+
- When `generateStaticPaths` is false, `generateStaticParams` returns `BUILD_VALIDATION_SITE` (`_DEFAULT_`) — not `return []` (Cache Components). Skip Edge in page/metadata for that site.
|
|
107
108
|
- Support preview mode when needed (uncached path)
|
|
108
109
|
|
|
109
110
|
Layout and Loading:
|
|
@@ -139,7 +140,7 @@ export default async function LocalizedPage() {
|
|
|
139
140
|
- Body shape (Sitecore Experience Edge / Content Operations):
|
|
140
141
|
- `updates[]`: publish-event rows; each `identifier` (with `-media` / `-layout` stripped) maps to `sc:item:<id>:<locale>:latest`
|
|
141
142
|
- `tags[]`: pass-through array. `sc:`-prefixed strings are revalidated verbatim (use this for ad-hoc / operational calls); bare item IDs are mapped to `sc:item:<id>:<defaultLocale>:latest`
|
|
142
|
-
- Dictionary tags from `sites` (`.sitecore/sites.json
|
|
143
|
+
- Dictionary tags from `sites` (`.sitecore/sites.json`; includes configured `defaultSite` from `generateSites` only when `NEXT_PUBLIC_DEFAULT_SITE_NAME` is set) are appended on every call
|
|
143
144
|
- Do not bypass auth or call `revalidateTag` directly from components
|
|
144
145
|
|
|
145
146
|
## Configuration
|
|
@@ -49,7 +49,8 @@ src/
|
|
|
49
49
|
|
|
50
50
|
### @sitecore-content-sdk
|
|
51
51
|
|
|
52
|
-
- Use `SitecoreClient` directly for preview, editing, and `getAppRouterStaticParams`
|
|
52
|
+
- Use `SitecoreClient` directly for preview, editing, and `getAppRouterStaticParams` (in `generateStaticParams` when `scConfig.generateStaticPaths` is true; when false return `{ site: BUILD_VALIDATION_SITE, locale, path: [] }` from `src/lib/sitecore-build-validation.ts` — Cache Components forbids `return []`)
|
|
53
|
+
- **Build validation:** `_DEFAULT_` is build-only — skip Edge in page/metadata when `isBuildValidationSite(site)`; segment `not-found.tsx` skips Edge when site is `_DEFAULT_`. Call `setCachedPageParams` before `notFound()` on real routes; read with `getCachedPageParams()` in segment not-found (do not use `headers()` there).
|
|
53
54
|
- Use the cache helpers in `src/lib/cache/` (e.g. `getSitecorePage`, `getSitecoreDictionary`, `getSitecoreErrorPage`) for cached reads — these wrap the client under `'use cache'` and attach Sitecore tags
|
|
54
55
|
- Implement proper error handling with try/catch blocks
|
|
55
56
|
- Handle content preview vs. published content scenarios — preview must remain **dynamic** (do not wrap preview in `'use cache'`)
|
|
@@ -77,7 +77,7 @@ These are the main head-app–specific concepts. Details are in the sections bel
|
|
|
77
77
|
- **`POST /api/revalidate`** is a single Sitecore-webhook endpoint. It accepts the Sitecore Experience Edge / Content Operations payload shape:
|
|
78
78
|
- `updates[]` — Sitecore publish-event rows; the handler maps each row's `identifier` (with `-media` / `-layout` stripped) to `sc:item:<id>:<locale>:latest`.
|
|
79
79
|
- `tags[]` — pass-through array. `sc:`-prefixed strings are revalidated verbatim (handy for ad-hoc, operational calls); bare item IDs are mapped to `sc:item:<id>:<defaultLocale>:latest`.
|
|
80
|
-
- Dictionary tags from `sites` (`.sitecore/sites.json
|
|
80
|
+
- Dictionary tags from `sites` (`.sitecore/sites.json`; configured `defaultSite` from `generateSites` only when `NEXT_PUBLIC_DEFAULT_SITE_NAME` is set) are merged on every call so dictionary changes are covered.
|
|
81
81
|
- **Auth (optional):** leave `SITECORE_REVALIDATE_SECRET` empty to skip auth (no `x-revalidate-secret` header). When set, callers must send the same value in `x-revalidate-secret` (configure that header on your Sitecore webhook).
|
|
82
82
|
- **Dictionary cache:** `sitecore.config.ts` disables the SDK's in-process dictionary cache (`dictionary: { caching: { enabled: false } }`). The Cache Components helper is the only dictionary cache layer, so `revalidateTag` works end to end.
|
|
83
83
|
|
|
@@ -90,7 +90,8 @@ These are the main head-app–specific concepts. Details are in the sections bel
|
|
|
90
90
|
### SitecoreClient
|
|
91
91
|
|
|
92
92
|
- **Where:** Single shared instance in `src/lib/sitecore-client.ts` — `new SitecoreClient({ ...scConfig })` with config from `sitecore.config.ts`.
|
|
93
|
-
- **Use directly for:** preview and editing (`getPreview`, `getDesignLibraryData`, internal editing routes), 500 page (`client.getErrorPage(ErrorPage.InternalServerError)` in `global-error.tsx`), and `getAppRouterStaticParams
|
|
93
|
+
- **Use directly for:** preview and editing (`getPreview`, `getDesignLibraryData`, internal editing routes), 500 page (`client.getErrorPage(ErrorPage.InternalServerError)` in `global-error.tsx`), and `getAppRouterStaticParams` when `generateStaticPaths` is true.
|
|
94
|
+
- **Build validation:** when `generateStaticPaths` is false, `generateStaticParams` returns `BUILD_VALIDATION_SITE` (`_DEFAULT_` from `src/lib/sitecore-build-validation.ts`); the page and `generateMetadata` skip Edge for that site; segment `not-found.tsx` skips Edge when site is `_DEFAULT_`. See SSG rules under Data fetching.
|
|
94
95
|
- **Use the cache helpers for everything else:** non-preview page reads go through `getSitecorePage`; dictionary reads through `getSitecoreDictionary`; 404 content through `getSitecoreErrorPage`. The cache helpers wrap the same client under `'use cache'` and attach the right tags.
|
|
95
96
|
- **Do not:** Create a second client or instantiate SitecoreClient elsewhere. Pass `site` and `locale` from route params (or `getCachedPageParams()` in the segment `not-found.tsx`, or `scConfig.defaultSite` / `scConfig.defaultLanguage` in the root `not-found.tsx`), not from global state.
|
|
96
97
|
|
|
@@ -131,7 +132,7 @@ These are the main head-app–specific concepts. Details are in the sections bel
|
|
|
131
132
|
|
|
132
133
|
### Multisite and Edge middleware (proxy)
|
|
133
134
|
|
|
134
|
-
- **Site list:** `.sitecore/sites.json` —
|
|
135
|
+
- **Site list:** `.sitecore/sites.json` — generated by the Sitecore CLI (`generateSites` in `sitecore.cli.config.ts`). Contains sites from Edge when multisite is enabled; prepends the configured default site **only** when `scConfig.defaultSite` is explicitly set (`NEXT_PUBLIC_DEFAULT_SITE_NAME`). Do not synthesize a fake site when env is unset. Used by middleware and API route handlers. Avoid hand-editing unless you know the format.
|
|
135
136
|
- **Edge middleware:** Implemented in **`src/proxy.ts`**. Next.js only runs middleware from a file named `middleware.ts` at root or in `src/`. If this app has only `proxy.ts`, add `src/middleware.ts` that re-exports it (e.g. `export { default } from './proxy';`) so the proxy runs.
|
|
136
137
|
- **Proxy chain (order is critical):** `defineProxy(preview, botTracking, locale, multisite, redirects, personalize).exec(req)`:
|
|
137
138
|
- **PreviewProxy** — authorizes preview requests on the internal editing host; no-op elsewhere.
|
|
@@ -148,8 +149,8 @@ These are the main head-app–specific concepts. Details are in the sections bel
|
|
|
148
149
|
- **Page data:** In the page (or a Server Component), use `getSitecorePage({ site, locale, path: path ?? [] })` from `src/lib/cache/get-sitecore-page.ts`. For preview, use `draftMode()`; if `draft.isEnabled`, call `client.getPreview(editingParams)` or `client.getDesignLibraryData(editingParams)` **directly on the SDK client** (preview must stay dynamic, not cached).
|
|
149
150
|
- **Dictionary:** Use `getSitecoreDictionary({ site, locale })` from `src/lib/cache/get-sitecore-dictionary.ts` (not `client.getDictionary` directly). This applies the `sc:dict:{site}:{locale}` tag so dictionary updates can be invalidated by webhook.
|
|
150
151
|
- **404 content:** Use `getSitecoreErrorPage({ site, locale, code: ErrorPage.NotFound })` from `src/lib/cache/get-sitecore-error-page.ts`. 500 content (in `global-error.tsx`) calls `client.getErrorPage(ErrorPage.InternalServerError, ...)` directly since `global-error.tsx` is a Client Component.
|
|
151
|
-
- **SSG:** `generateStaticParams
|
|
152
|
-
- **Metadata:** `generateMetadata` in the same segment calls `getSitecorePage` so it hits the same cache entry as the page.
|
|
152
|
+
- **SSG:** In `generateStaticParams`, call `client.getAppRouterStaticParams(sites, routing.locales)` (sites from `.sitecore/sites.json`) only when `process.env.NODE_ENV !== 'development'` and `scConfig.generateStaticPaths` is true. When `generateStaticPaths` is false, return one param `{ site: BUILD_VALIDATION_SITE, locale, path: [] }` from `src/lib/sitecore-build-validation.ts` (`_DEFAULT_`) — a **build-only placeholder**, not a real Sitecore site. Cache Components forbids `return []` ([empty-generate-static-params](https://nextjs.org/docs/messages/empty-generate-static-params)). The catch-all page must call `isBuildValidationSite(site)` and skip Edge (seed `setCachedPageParams`, then `notFound()`); `generateMetadata` must skip Edge for the same site. Do not use `sites[0]` or hardcoded `'default'` for this fallback.
|
|
153
|
+
- **Metadata:** `generateMetadata` in the same segment calls `getSitecorePage` (except for `BUILD_VALIDATION_SITE`) so it hits the same cache entry as the page render. Segment `not-found.tsx` skips Edge when `isBuildValidationSite(resolvedSite)`.
|
|
153
154
|
|
|
154
155
|
### On-demand revalidation (`POST /api/revalidate`)
|
|
155
156
|
|
|
@@ -169,8 +170,8 @@ These are the main head-app–specific concepts. Details are in the sections bel
|
|
|
169
170
|
This template ships **two** not-found components and a segment layout that ties them together while staying compatible with SSG and Cache Components:
|
|
170
171
|
|
|
171
172
|
- **Root not-found:** `src/app/not-found.tsx`. Used as the fallback when no segment handles the route (e.g. unknown site/locale). Falls back to `scConfig.defaultSite` / `scConfig.defaultLanguage` and calls `getSitecoreErrorPage({ site, locale, code: ErrorPage.NotFound })`, so 404 content gets the same Sitecore cache tags as a normal page.
|
|
172
|
-
- **Segment not-found:** `src/app/[site]/[locale]/[[...path]]/not-found.tsx`. Triggered when the catch-all page calls `notFound()` (
|
|
173
|
-
- **Segment layout:** `src/app/[site]/[locale]/[[...path]]/layout.tsx`. Calls `setCachedPageParams({ site, locale })` on every request for this segment.
|
|
173
|
+
- **Segment not-found:** `src/app/[site]/[locale]/[[...path]]/not-found.tsx`. Triggered when the catch-all page calls `notFound()`. Reads site/locale via `getCachedPageParams()` (set by the segment layout and by the page before `notFound()`). If site is empty or `isBuildValidationSite(resolvedSite)` (`_DEFAULT_`), returns static fallback HTML without calling Edge. Otherwise calls `getSitecoreErrorPage(...)`. Wrapped in `NextIntlClientProvider` to keep i18n working in 404 markup.
|
|
174
|
+
- **Segment layout:** `src/app/[site]/[locale]/[[...path]]/layout.tsx`. Calls `setCachedPageParams({ site, locale })` on every request for this segment. The catch-all **page** also calls `setCachedPageParams` immediately before `notFound()` so early 404s during SSG still pass site/locale to segment not-found. Uses the SDK's React `cache()` based `set/getCachedPageParams` helpers (from `@sitecore-content-sdk/nextjs`) so segment `not-found.tsx` can read `{ site, locale }` **without** calling `headers()` — which would opt the route out of SSG. **Do not** call `headers()` in the segment not-found; keep using `getCachedPageParams()`.
|
|
174
175
|
- **Root global error:** `src/app/global-error.tsx` is a Client Component (`'use client'`) that fetches `client.getErrorPage(ErrorPage.InternalServerError, ...)` on the client; it is not cached (the cache helpers are server-side).
|
|
175
176
|
|
|
176
177
|
### API route handlers
|
|
@@ -216,7 +217,8 @@ This template ships **two** not-found components and a segment layout that ties
|
|
|
216
217
|
| Set `SITECORE_REVALIDATE_SECRET` and send `x-revalidate-secret` when you want the endpoint protected | Hardcode the revalidate secret or expose it client-side |
|
|
217
218
|
| Keep `sitecore.config.ts` dictionary cache disabled | Re-enable the SDK in-process dictionary cache (bypasses `revalidateTag`) |
|
|
218
219
|
| Use Server Components for async data fetching | Put async data fetching in client components when SSR is intended |
|
|
219
|
-
| Set site/locale via `setCachedPageParams` in
|
|
220
|
+
| Set site/locale via `setCachedPageParams` in segment layout **and** in the page before `notFound()`; read with `getCachedPageParams()` in segment `not-found.tsx` | Call `headers()` in not-found (opts out of SSG) or hardcode site/locale |
|
|
221
|
+
| Use `BUILD_VALIDATION_SITE` (`_DEFAULT_`) when `generateStaticPaths` is false (Cache Components); skip Edge for that site in page/metadata/not-found | `return []` from `generateStaticParams`, or use `sites[0]` / `'default'` as build fallback |
|
|
220
222
|
| Use createXRouteHandler and `.sitecore/sites.json` for sitemap/robots | Hardcode site list or commit `.env` |
|
|
221
223
|
| Use Sitecore field components and validate fields | Expose API keys or editing secret in client code |
|
|
222
224
|
| Document required env vars in `.env.example` only | Commit `.env` or `.env.local` |
|
|
@@ -226,7 +228,7 @@ This template ships **two** not-found components and a segment layout that ties
|
|
|
226
228
|
|
|
227
229
|
## Guardrails for agentic AI
|
|
228
230
|
|
|
229
|
-
- **Preserve behavior:** Do not change the proxy order (PreviewProxy → BotTrackingProxy → LocaleProxy → AppRouterMultisiteProxy → …), the `[site]/[locale]/[[...path]]` route shape, the `{site}_{locale}` next-intl convention, the cache-helper boundary (cache helpers wrap non-preview Sitecore reads; preview/editing use `client.*` directly), or the `setCachedPageParams` → `getCachedPageParams` flow between the segment layout and segment `not-found.tsx` (this is what keeps the 404 SSG-safe). Preserve `draftMode` handling in layout and page.
|
|
231
|
+
- **Preserve behavior:** Do not change the proxy order (PreviewProxy → BotTrackingProxy → LocaleProxy → AppRouterMultisiteProxy → …), the `[site]/[locale]/[[...path]]` route shape, the `{site}_{locale}` next-intl convention, the cache-helper boundary (cache helpers wrap non-preview Sitecore reads; preview/editing use `client.*` directly), the `BUILD_VALIDATION_SITE` build-validation flow, or the `setCachedPageParams` → `getCachedPageParams` flow between the segment layout, page, and segment `not-found.tsx` (this is what keeps the 404 SSG-safe). Preserve `draftMode` handling in layout and page.
|
|
230
232
|
- **Do not expand scope:** Limit edits to the app (app router, components, API routes, cache helpers, i18n, config). Do not modify SDK packages or monorepo tooling unless explicitly asked. Do not change CI, lockfiles, or root config.
|
|
231
233
|
- **Follow existing patterns:** When adding routes, layouts, or components, mirror the existing structure. Use the same Sitecore client, cache helpers, component maps, and env-based config. Do not introduce a different way to resolve site/locale, a second client, or a parallel cache layer.
|
|
232
234
|
- **Verify and stay safe:** After edits, the app should build with `npm run build`. Do not commit secrets or `.env`; only document variables in `.env.example`. Do not add npm dependencies without explicit approval. When in doubt, prefer the existing implementation and ask for clarification.
|
|
@@ -32,11 +32,11 @@ Using SDK field components: `<Text>`, `<RichText>`, `<Image>`, `<Link>`, with pr
|
|
|
32
32
|
|
|
33
33
|
### content-sdk-graphql-data-fetching
|
|
34
34
|
|
|
35
|
-
Page and dictionary fetching via the cache helpers in `src/lib/cache/`. Use `getSitecorePage({ site, locale, path })`, `getSitecoreDictionary({ site, locale })`, `getSitecoreErrorPage({ site, locale, code })` for cached reads with Sitecore tags. Use `client.getPreview` / `client.getDesignLibraryData` directly for preview
|
|
35
|
+
Page and dictionary fetching via the cache helpers in `src/lib/cache/`. Use `getSitecorePage({ site, locale, path })`, `getSitecoreDictionary({ site, locale })`, `getSitecoreErrorPage({ site, locale, code })` for cached reads with Sitecore tags. Use `client.getPreview` / `client.getDesignLibraryData` directly for preview. In `generateStaticParams`, call `client.getAppRouterStaticParams` when `scConfig.generateStaticPaths` is true; when false, return `{ site: BUILD_VALIDATION_SITE, locale, path: [] }` from `src/lib/sitecore-build-validation.ts` (not `return []`). Skip Edge in page/metadata/not-found when `isBuildValidationSite(site)`.
|
|
36
36
|
|
|
37
37
|
### content-sdk-route-configuration
|
|
38
38
|
|
|
39
|
-
Routing: single catch-all at `src/app/[site]/[locale]/[[...path]]/page.tsx`. Layout chain: `app/layout.tsx` → `app/[site]/layout.tsx` (Bootstrap, draftMode) → `app/[site]/[locale]/[[...path]]/layout.tsx` (calls `setCachedPageParams({ site, locale })`) → page. Call `setRequestLocale(\`${site}_${locale}\`)`
|
|
39
|
+
Routing: single catch-all at `src/app/[site]/[locale]/[[...path]]/page.tsx`. Layout chain: `app/layout.tsx` → `app/[site]/layout.tsx` (Bootstrap, draftMode) → `app/[site]/[locale]/[[...path]]/layout.tsx` (calls `setCachedPageParams({ site, locale })`) → page. Call `setRequestLocale(\`${site}_${locale}\`)` in the page. **Two not-founds**: segment `[[...path]]/not-found.tsx` reads `getCachedPageParams()` (set by layout and by the page before `notFound()`); skips Edge for `BUILD_VALIDATION_SITE` (`_DEFAULT_`) on site, or when site is empty; otherwise calls `getSitecoreErrorPage`. Root `src/app/not-found.tsx` falls back to `scConfig.defaultSite` / `scConfig.defaultLanguage`. `global-error.tsx` uses `client.getErrorPage` directly (Client Component, not cached).
|
|
40
40
|
|
|
41
41
|
### content-sdk-site-setup-and-env
|
|
42
42
|
|
|
@@ -44,7 +44,7 @@ Site and environment: `sitecore.config.ts`, environment variables, default site
|
|
|
44
44
|
|
|
45
45
|
### content-sdk-multisite-management
|
|
46
46
|
|
|
47
|
-
Multisite: `.sitecore/sites.json
|
|
47
|
+
Multisite: `.sitecore/sites.json` (CLI `generateSites` — Edge sites plus configured `defaultSite` only when `NEXT_PUBLIC_DEFAULT_SITE_NAME` is set), proxy in `src/proxy.ts`. Chain order is **fixed:** PreviewProxy → BotTrackingProxy → LocaleProxy → AppRouterMultisiteProxy → RedirectsProxy → PersonalizeProxy. Do not change proxy order.
|
|
48
48
|
|
|
49
49
|
### content-sdk-dictionary-and-i18n
|
|
50
50
|
|
|
@@ -44,7 +44,8 @@ src/
|
|
|
44
44
|
## Library Usage
|
|
45
45
|
|
|
46
46
|
### @sitecore-content-sdk
|
|
47
|
-
- Use `SitecoreClient` for content fetching (directly for preview/editing/
|
|
47
|
+
- Use `SitecoreClient` for content fetching (directly for preview/editing and `getAppRouterStaticParams` when `scConfig.generateStaticPaths` is true; when false return `BUILD_VALIDATION_SITE` from `src/lib/sitecore-build-validation.ts` for Cache Components build validation; indirectly via cache helpers for cached reads)
|
|
48
|
+
- **Build validation:** `_DEFAULT_` is build-only — skip Edge in page/metadata when `isBuildValidationSite(site)`; segment `not-found.tsx` skips Edge when site is `_DEFAULT_`. Call `setCachedPageParams` before `notFound()` on real routes; read with `getCachedPageParams()` in segment not-found (do not use `headers()` there).
|
|
48
49
|
- Implement proper error handling with try/catch blocks
|
|
49
50
|
- Handle content preview vs. published content scenarios — preview must remain **dynamic** (do not wrap preview in `'use cache'`)
|
|
50
51
|
|
|
@@ -2,16 +2,29 @@ import Link from 'next/link';
|
|
|
2
2
|
import { ErrorPage, getCachedPageParams } from '@sitecore-content-sdk/nextjs';
|
|
3
3
|
import { getSitecoreErrorPage } from 'lib/cache/get-sitecore-error-page';
|
|
4
4
|
import scConfig from 'sitecore.config';
|
|
5
|
+
import { isBuildValidationSite } from 'src/lib/sitecore-build-validation';
|
|
5
6
|
import Layout from 'src/Layout';
|
|
6
7
|
import Providers from 'src/Providers';
|
|
7
8
|
import { NextIntlClientProvider } from 'next-intl';
|
|
8
9
|
import { setRequestLocale } from 'next-intl/server';
|
|
9
10
|
|
|
11
|
+
const staticNotFound = (
|
|
12
|
+
<div style={{ padding: 10 }}>
|
|
13
|
+
<h1>Page not found</h1>
|
|
14
|
+
<p>This page does not exist.</p>
|
|
15
|
+
<Link href="/">Go to the Home page</Link>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
|
|
10
19
|
export default async function NotFound() {
|
|
11
20
|
const { site, locale } = getCachedPageParams();
|
|
12
|
-
const resolvedSite = site || scConfig.defaultSite;
|
|
21
|
+
const resolvedSite = (site || scConfig.defaultSite)?.trim();
|
|
13
22
|
const resolvedLocale = locale || scConfig.defaultLanguage;
|
|
14
23
|
|
|
24
|
+
if (!resolvedSite || isBuildValidationSite(resolvedSite)) {
|
|
25
|
+
return staticNotFound;
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
const page = await getSitecoreErrorPage({
|
|
16
29
|
site: resolvedSite,
|
|
17
30
|
locale: resolvedLocale,
|
|
@@ -32,11 +45,5 @@ export default async function NotFound() {
|
|
|
32
45
|
);
|
|
33
46
|
}
|
|
34
47
|
|
|
35
|
-
return
|
|
36
|
-
<div style={{ padding: 10 }}>
|
|
37
|
-
<h1>Page not found</h1>
|
|
38
|
-
<p>This page does not exist.</p>
|
|
39
|
-
<Link href="/">Go to the Home page</Link>
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
48
|
+
return staticNotFound;
|
|
42
49
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDesignLibraryPreviewData } from '@sitecore-content-sdk/nextjs/editing';
|
|
2
|
+
import { setCachedPageParams } from '@sitecore-content-sdk/nextjs';
|
|
2
3
|
import { notFound } from 'next/navigation';
|
|
3
4
|
import { draftMode } from 'next/headers';
|
|
4
5
|
<% if (prerender === 'SSG') { -%>
|
|
@@ -9,6 +10,7 @@ import scConfig from 'sitecore.config';
|
|
|
9
10
|
<% } -%>
|
|
10
11
|
import client from 'src/lib/sitecore-client';
|
|
11
12
|
import { getSitecorePage } from 'src/lib/cache/get-sitecore-page';
|
|
13
|
+
import { BUILD_VALIDATION_SITE, isBuildValidationSite } from 'src/lib/sitecore-build-validation';
|
|
12
14
|
import Layout, { RouteFields } from 'src/Layout';
|
|
13
15
|
import Providers from 'src/Providers';
|
|
14
16
|
import { NextIntlClientProvider } from 'next-intl';
|
|
@@ -22,10 +24,16 @@ type PageProps = {
|
|
|
22
24
|
export default async function Page({ params, searchParams }: PageProps) {
|
|
23
25
|
const { site, locale, path } = await params;
|
|
24
26
|
|
|
27
|
+
if (isBuildValidationSite(site)) {
|
|
28
|
+
setCachedPageParams({ site, locale });
|
|
29
|
+
notFound();
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
// Cached fetch first so missing routes can notFound() without dynamic APIs in the ancestor tree.
|
|
26
33
|
const cachedPage = await getSitecorePage({ site, locale, path: path ?? [] });
|
|
27
34
|
|
|
28
35
|
if (!cachedPage) {
|
|
36
|
+
setCachedPageParams({ site, locale });
|
|
29
37
|
notFound();
|
|
30
38
|
}
|
|
31
39
|
|
|
@@ -49,6 +57,7 @@ export default async function Page({ params, searchParams }: PageProps) {
|
|
|
49
57
|
|
|
50
58
|
// If the page is not found, return a 404
|
|
51
59
|
if (!page) {
|
|
60
|
+
setCachedPageParams({ site, locale });
|
|
52
61
|
notFound();
|
|
53
62
|
}
|
|
54
63
|
|
|
@@ -71,11 +80,9 @@ export const generateStaticParams = async () => {
|
|
|
71
80
|
routing.locales.slice()
|
|
72
81
|
);
|
|
73
82
|
}
|
|
74
|
-
// Next.js 16 requires at least one result
|
|
75
|
-
// Return a default param for the root page
|
|
76
83
|
return [
|
|
77
84
|
{
|
|
78
|
-
site:
|
|
85
|
+
site: BUILD_VALIDATION_SITE,
|
|
79
86
|
locale: routing.defaultLocale || scConfig.defaultLanguage,
|
|
80
87
|
path: [],
|
|
81
88
|
},
|
|
@@ -86,6 +93,10 @@ export const generateStaticParams = async () => {
|
|
|
86
93
|
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
|
|
87
94
|
const { path, site, locale } = await params;
|
|
88
95
|
|
|
96
|
+
if (isBuildValidationSite(site)) {
|
|
97
|
+
return { title: 'Page' };
|
|
98
|
+
}
|
|
99
|
+
|
|
89
100
|
const draft = await draftMode();
|
|
90
101
|
|
|
91
102
|
let page;
|
package/dist/templates/nextjs-app-router-cache-components/src/lib/sitecore-build-validation.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is a placeholder `[site]` segment used only for Next.js Cache Components build-time validation
|
|
3
|
+
* when `generateStaticPaths` is false. Not a real Sitecore site — routes using this value
|
|
4
|
+
* skip Edge fetches and render the static not-found fallback so `next build` can succeed
|
|
5
|
+
* without a configured default site or content configured in Sitecore AI.
|
|
6
|
+
* One use case for this is running your application as an editing host when no sites are configured.
|
|
7
|
+
*
|
|
8
|
+
* @see https://nextjs.org/docs/messages/empty-generate-static-params
|
|
9
|
+
*/
|
|
10
|
+
export const BUILD_VALIDATION_SITE = '_DEFAULT_' as const;
|
|
11
|
+
|
|
12
|
+
export function isBuildValidationSite(site: string | undefined): boolean {
|
|
13
|
+
return site === BUILD_VALIDATION_SITE;
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-content-sdk-app",
|
|
3
|
-
"version": "2.2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Sitecore Content SDK initializer",
|
|
5
5
|
"bin": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
"minimist": "^1.2.8"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@sitecore-content-sdk/analytics-core": "2.1.
|
|
42
|
-
"@sitecore-content-sdk/cli": "2.2.0
|
|
43
|
-
"@sitecore-content-sdk/events": "2.1.
|
|
44
|
-
"@sitecore-content-sdk/nextjs": "2.2.0
|
|
45
|
-
"@sitecore-content-sdk/personalize": "2.1.
|
|
41
|
+
"@sitecore-content-sdk/analytics-core": "^2.1.0",
|
|
42
|
+
"@sitecore-content-sdk/cli": "^2.2.0",
|
|
43
|
+
"@sitecore-content-sdk/events": "^2.1.0",
|
|
44
|
+
"@sitecore-content-sdk/nextjs": "^2.2.0",
|
|
45
|
+
"@sitecore-content-sdk/personalize": "^2.1.0",
|
|
46
46
|
"@stylistic/eslint-plugin": "^5.2.2",
|
|
47
47
|
"@types/chai": "^5.2.2",
|
|
48
48
|
"@types/cross-spawn": "^6.0.6",
|