riverbankcms 0.1.0 → 0.2.2

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.
@@ -1,6 +1,8 @@
1
1
  ## RiverbankCMS SDK Context
2
2
 
3
- This repository is a site using **RiverbankCMS** as a headless CMS via the `@riverbankcms/sdk` package.
3
+ This repository is a fully headless site using **RiverbankCMS** as a CMS via
4
+ the `@riverbankcms/sdk` package. The host owns markup, styles, and block
5
+ rendering through the override registry.
4
6
 
5
7
  ### What This Means
6
8
 
@@ -23,7 +25,7 @@ Detailed documentation is available in `.riverbank/docs/`:
23
25
  - `content-management.md` - Content model and sync workflows
24
26
  - `schema.md` - Site-specific schema (auto-generated from config)
25
27
  - `block-types.md` - Available block types (auto-generated)
26
- - `theme-schema.md` - Theme schema reference (auto-generated from Zod)
28
+ - `theme-schema.md` - Optional theme schema reference (auto-generated from Zod)
27
29
  - `workflows/` - Step-by-step guides for common tasks, including editor workflows
28
30
  - `workflows/agent-safe-sync.md` - Agent-safe dry-run, scoped push, and `--env` guidance
29
31
  - `site-workflows/` - Site-specific workflows and customizations
@@ -46,6 +48,11 @@ Use CLI `--env=remote` / `--env=local` for CMS commands rather than relying on `
46
48
  3. **Preview**: `riverbankcms push --dry-run` shows what would change
47
49
  4. **Push**: `riverbankcms push` applies changes to CMS
48
50
 
51
+ Built-in semantic kinds such as `block.hero` remain in content JSON. Register
52
+ the host-owned override for every kind the site uses in `src/lib/block-overrides.tsx`.
53
+ The generated default uses `SITE_RENDERING` with override-only rendering and no
54
+ CMS theme runtime.
55
+
49
56
  For narrow edits, push the narrowest scope possible, for example `riverbankcms push pages privacy-policy --env=remote --dry-run`. If broad local content is stale or contains unrelated differences, see `.riverbank/docs/workflows/agent-safe-sync.md`.
50
57
 
51
58
  ### Editing Page Content Directly
@@ -10,7 +10,7 @@ All commands support these options:
10
10
  | ---------------- | ---------------------------------------------------------- |
11
11
  | `--json` | Output a single JSON envelope for machine parsing |
12
12
  | `--quiet` | Minimal output (suppress non-essential messages) |
13
- | `--env <target>` | Target environment: `local` (default), `remote`, or `both` |
13
+ | `--env <target>` | Target project environment: `local` (default), `remote`, or `both` |
14
14
  | `--remote` | **[Deprecated]** Use `--env=remote` instead |
15
15
 
16
16
  **The `--json` envelope (single-document contract):** under `--json`, *every* invocation emits **exactly one** JSON document on stdout:
@@ -25,8 +25,8 @@ All commands support these options:
25
25
 
26
26
  **Environment targeting:**
27
27
 
28
- - `--env=local` (default): Target local Supabase/development environment
29
- - `--env=remote`: Target production CMS
28
+ - `--env=local` (default): Target the local development CMS
29
+ - `--env=remote`: Target the production CMS
30
30
  - `--env=both`: Run against both environments sequentially
31
31
 
32
32
  **Safety behavior:**
@@ -67,12 +67,55 @@ RIVERBANK_REMOTE_API_KEY=bld_live_sk_... # Required for deploy command
67
67
 
68
68
  - Management API keys start with `bld_mgmt_sk_` (for write operations)
69
69
  - Content API keys start with `bld_live_sk_` (for read-only operations)
70
- - `RIVERBANK_*_SUPABASE_URL` is deprecated and ignored by media sync
71
70
 
72
71
  ---
73
72
 
74
73
  ## Setup Commands
75
74
 
75
+ ### Account authentication and site setup
76
+
77
+ Account commands use a provider-neutral Riverbank account session. Select the
78
+ account environment independently from the project `--env` target:
79
+
80
+ Production account OAuth is deliberately rollout-disabled. The examples below
81
+ use staging; use local for development. Do not use
82
+ `--account-env=production` until the production account rollout is explicitly
83
+ enabled.
84
+
85
+ ```bash
86
+ riverbankcms login --account-env=staging
87
+ riverbankcms whoami --account-env=staging
88
+ riverbankcms sites list --account-env=staging
89
+ riverbankcms sites create --account-env=staging --name "Dovetail" --yes
90
+ riverbankcms logout --account-env=staging
91
+ riverbankcms auth revoke --account-env=staging --yes
92
+ ```
93
+
94
+ The CLI stores the refresh session in the OS credential store when available.
95
+ If a locked-file fallback is required, the CLI explains the risk and requires
96
+ `--allow-locked-file`. `logout` removes this machine’s stored session;
97
+ `auth revoke` revokes the account grant so access tokens on other machines can
98
+ no longer refresh. Neither operation removes sites, project credentials, or
99
+ content.
100
+
101
+ Use `link` for an existing project and `init` for a complete new project:
102
+
103
+ ```bash
104
+ riverbankcms link --project ./my-site --account-env=staging --site <site-id> --preview
105
+ riverbankcms init ./my-site --account-env=staging --name "Dovetail" --preview
106
+ ```
107
+
108
+ Preview is explicit state. `--preview` includes preview routes and a preview
109
+ credential; `--no-preview` omits both. Interactive `init` asks for the choice;
110
+ JSON and other non-interactive mutations require an explicit choice and
111
+ `--yes`. Published CLI projects install packages from npm. Local/tarball
112
+ sources are test-fixture seams and are not public flags.
113
+
114
+ Generated projects are provider-neutral and contain only Riverbank project
115
+ configuration. The account refresh session is never written to the project
116
+ env file, and no database-provider URL, provider auth token, or service-role
117
+ credential is scaffolded.
118
+
76
119
  ### setup plan
77
120
 
78
121
  Show the safe site, key, user access, and env setup lifecycle for the selected environment.
@@ -88,10 +131,31 @@ riverbankcms setup plan --env=remote
88
131
  riverbankcms setup plan --env=both --json
89
132
  ```
90
133
 
91
- The setup planner is read-only. It does not create sites, issue API keys, or grant dashboard access. Those operations require a dashboard/admin actor or an approved CMS repo script with service-role/admin credentials. SDK management keys are site-scoped and are only used for verifying access and syncing content/config for an existing site.
134
+ The setup planner is read-only. It reports which values are missing and shows
135
+ the supported lifecycle. Use `riverbankcms sites create`, `riverbankcms link`,
136
+ or `riverbankcms init` with an authenticated account session to create sites
137
+ and issue project credentials for the signed-in account. Use the dashboard to
138
+ grant access to additional users. SDK management keys are site-scoped and are
139
+ used for project sync; they cannot create sites or grant other users access.
92
140
 
93
141
  Use this command when a project has missing `RIVERBANK_*_SITE_ID`, `RIVERBANK_*_DASHBOARD_URL`, or `RIVERBANK_*_MGMT_API_KEY` values, or when onboarding an existing remote site into an SDK repo.
94
142
 
143
+ ### Post-deployment revalidation
144
+
145
+ `init` scaffolds `/api/revalidate` but does not register a webhook or write a
146
+ webhook secret. After the project has a public HTTPS origin, activate it
147
+ explicitly:
148
+
149
+ ```bash
150
+ riverbankcms webhooks setup \
151
+ --env=remote \
152
+ --url=https://www.example.com/api/revalidate \
153
+ --yes
154
+ ```
155
+
156
+ Until setup is complete, the generated site’s ISR interval is the freshness
157
+ fallback. Keep the one-time webhook secret in the deployment environment.
158
+
95
159
  ---
96
160
 
97
161
  ## Content Sync Commands
@@ -914,14 +978,14 @@ riverbankcms event get summer-fest
914
978
  riverbankcms event upsert summer-fest --file ./content/events/summer-fest.json
915
979
  riverbankcms event cancel summer-fest
916
980
  riverbankcms event delete summer-fest --yes
917
- riverbankcms event occurrence list summer-fest
918
- riverbankcms event occurrence add summer-fest \
981
+ riverbankcms event occurrence list summer-fest summer-run
982
+ riverbankcms event occurrence add summer-fest summer-run \
919
983
  --starts-at 2026-08-01T10:00 \
920
984
  --duration-minutes 60
921
- riverbankcms event occurrence update summer-fest <occurrence-uuid> \
985
+ riverbankcms event occurrence update summer-fest summer-run <occurrence-uuid> \
922
986
  --capacity 20
923
- riverbankcms event occurrence cancel summer-fest <occurrence-uuid>
924
- riverbankcms event occurrence delete summer-fest <occurrence-uuid> --yes
987
+ riverbankcms event occurrence cancel summer-fest summer-run <occurrence-uuid>
988
+ riverbankcms event occurrence delete summer-fest summer-run <occurrence-uuid> --yes
925
989
  ```
926
990
 
927
991
  `event upsert` and `push events` accept canonical event files with `path` and `blocks`. Events use native page authority: an `eventOffering` or `eventSeries` subject owns a page surface, and that surface owns its route, field content, publication state, and slots. `path` updates the page-surface route. `blocks` maps slot keys to surface-slot blocks, for example `blocks.main` for the built-in event template. Use stable block identifiers so repeat pushes update existing slot blocks safely. Unknown slot keys or block kinds fail validation; default slots accept enabled SDK `custom.*` blocks, while explicit template `allowedBlocks` lists remain authoritative. Ordinary editorial entries continue to use the separate `entry` commands and content-entry model described above.
@@ -932,7 +996,9 @@ riverbankcms event occurrence delete summer-fest <occurrence-uuid> --yes
932
996
  series. Mutation commands always use the occurrence UUID shown by `list`; a
933
997
  calendar date is never accepted as an identity.
934
998
 
935
- - `list <event-identifier>` shows all future dates, including cancelled dates.
999
+ - `list <event-identifier> <run-identifier>` shows all future dates, including
1000
+ cancelled dates. The parent Event and run are both explicit, so multi-run
1001
+ Events never rely on an ambiguous identifier.
936
1002
  Add `--all` to include history or `--status scheduled|cancelled|completed` to
937
1003
  filter. The CLI follows the API's bounded pages, while `--json` keeps the
938
1004
  canonical UTC instants in its single result envelope.
@@ -75,16 +75,19 @@ content/
75
75
  navigation.json # All navigation menus
76
76
  ```
77
77
 
78
- ### 5. Themes
78
+ ### 5. Themes (Optional Opt-in)
79
79
 
80
- Site theme objects used for rendering blocks.
80
+ CMS theme objects are optional. Fully headless sites use host-owned markup,
81
+ styles, and block overrides instead of loading a CMS theme or SDK runtime
82
+ styles. Use a theme only when the site intentionally opts into themed built-in
83
+ rendering or local theme iteration.
81
84
 
82
85
  **Local file structure:**
83
86
 
84
87
  ```
85
88
  content/
86
89
  themes/
87
- current.json # Default theme (pulled from CMS or edited locally)
90
+ current.json # Optional theme (pulled from CMS or edited locally)
88
91
  ```
89
92
 
90
93
  ### 6. Footer Content
@@ -130,6 +133,12 @@ content/
130
133
  yoga-class.json # Event with schedule, pricing, venue/category refs
131
134
  ```
132
135
 
136
+ Event registration keeps the semantic `block.event-registration` kind. In the
137
+ fully headless default, register a host-owned `event-registration` override and
138
+ own its markup, accessibility, and styles. Use the public
139
+ `useHeadlessEventRegistrationFlow` controller from `@riverbankcms/sdk/bookings`
140
+ where it fits; controller support varies by functional block.
141
+
133
142
  ---
134
143
 
135
144
  ## Working with Identifiers
@@ -1,6 +1,6 @@
1
1
  # Getting Started with RiverbankCMS SDK
2
2
 
3
- This site uses RiverbankCMS as a headless CMS. Content is managed through the Riverbank CLI.
3
+ This site uses RiverbankCMS as a fully headless CMS. Content is managed through the Riverbank CLI, while the host owns markup, styles, and block rendering.
4
4
 
5
5
  ## Quick Setup
6
6
 
@@ -36,12 +36,10 @@ content/
36
36
  │ └── [content-type].json
37
37
  ├── pages/
38
38
  │ └── [page-identifier].json
39
- ├── themes/
40
- │ └── current.json
41
39
  └── navigation.json
42
40
  ```
43
41
 
44
- You can also pull just the theme:
42
+ Themes are optional. Only add `content/themes/` when this site explicitly opts into CMS/local theme rendering; fully headless sites use the host-owned `SITE_RENDERING` override-only configuration and do not need a theme file. The CLI still supports pulling a theme when you choose that opt-in:
45
43
 
46
44
  ```bash
47
45
  riverbankcms pull theme
@@ -76,11 +74,11 @@ Apply changes to the CMS:
76
74
  riverbankcms push
77
75
  ```
78
76
 
79
- ## Local Theme Iteration (Edge-safe)
77
+ ## Optional Local Theme Iteration (Edge-safe)
80
78
 
81
- If your SDK site uses `createCatchAllPage()`, you can render with local theme objects (and switch themes at runtime) without pushing theme changes to the CMS. See `cli-reference.md` for theme pull/push commands and the SDK README for the `createCatchAllPage({ themes, themeSwitch })` API.
79
+ Fully headless sites do not load CMS themes or SDK runtime styles by default. If a site intentionally opts into themed built-in rendering, `createCatchAllPage()` can render local theme objects (and switch themes at runtime) without pushing theme changes to the CMS. See `cli-reference.md` for the legitimate theme pull/push commands and the SDK README for the `createCatchAllPage({ themes, themeSwitch })` API.
82
80
 
83
- Event registration blocks need both the SDK base stylesheet and a runtime theme scope. Generated SDK pages that render inside `Layout` get `ThemeScope` by default. Standalone `Page` or `Block` usage should wrap registration content in `ThemeBridgeProvider` or manual `generateThemeBridgeCss` with a `data-theme-scope="sdk"` wrapper; keep visual changes in scoped CSS variables instead of forking booking logic.
81
+ Event registration keeps the semantic `block.event-registration` kind, but its UI is host-owned in the fully headless default. Register an `event-registration` override in `src/lib/block-overrides.tsx` and use the public `useHeadlessEventRegistrationFlow` controller from `@riverbankcms/sdk/bookings` where it fits; own the markup, accessibility, and styles in the host. Other functional blocks may have different public controller support, so check their SDK surface rather than assuming a controller exists for every kind.
84
82
 
85
83
  ## Key Documentation
86
84
 
@@ -90,7 +88,7 @@ Event registration blocks need both the SDK base stylesheet and a runtime theme
90
88
  | `content-management.md` | How content is structured |
91
89
  | `schema.md` | This site's content types and blocks (auto-generated) |
92
90
  | `block-types.md` | Available block types (auto-generated) |
93
- | `theme-schema.md` | Theme schema reference (auto-generated from Zod) |
91
+ | `theme-schema.md` | Optional theme schema reference (auto-generated from Zod) |
94
92
  | `workflows/` | Step-by-step guides for common tasks, including editor workflows |
95
93
  | `workflows/agent-safe-sync.md` | Agent-safe dry-run, scoped push, and stale-content handling |
96
94
 
@@ -127,13 +125,13 @@ Additional content configuration for scaffolding.
127
125
 
128
126
  ## Working with Remote/Production
129
127
 
130
- If the remote site, management key, or dashboard user access is not set up yet, start with the safe setup planner:
128
+ If the remote site or project management key is not set up yet, start with the safe setup planner:
131
129
 
132
130
  ```bash
133
131
  riverbankcms setup plan --env=remote
134
132
  ```
135
133
 
136
- See `workflows/remote-setup.md` for the full site/key/user lifecycle. The Riverbank CLI uses site-scoped management keys for sync; remote site creation, key issuance, and user access changes stay in the dashboard/admin surface.
134
+ See `workflows/remote-setup.md` for the full site/key/user lifecycle. The authenticated Riverbank CLI can create a site and link project credentials for the signed-in account. Use the dashboard to grant access to additional users.
137
135
 
138
136
  Use `--env=remote` to target production instead of local:
139
137
 
@@ -2,6 +2,13 @@
2
2
 
3
3
  This workflow guides you through adding a block to an existing page.
4
4
 
5
+ Generated sites use fully headless, override-only rendering by default. Keep
6
+ the built-in semantic kind in CMS content (for example, `block.hero` or
7
+ `block.body-text`), then register the host-owned renderer for every kind the
8
+ site uses in `src/lib/block-overrides.tsx`. The registry uses short semantic
9
+ keys such as `hero`; an unregistered kind raises `MissingBlockOverrideError`
10
+ instead of falling back to a built-in renderer.
11
+
5
12
  ## Prerequisites
6
13
 
7
14
  - Page already exists in the CMS
@@ -25,7 +32,7 @@ View built-in blocks in `block-types.md`. Common block kinds:
25
32
 
26
33
  Check `schema.md` for custom blocks specific to this site.
27
34
 
28
- Prefer built-in blocks before creating a custom block. For a simple policy/terms/about page, use `block.body-text` and put the page title, intro, and section headings in the rich text body. Only create a custom block when the needed structure or behavior cannot be represented by built-in blocks plus styling.
35
+ Prefer built-in blocks before creating a custom block. For a simple policy/terms/about page, use `block.body-text` and put the page title, intro, and section headings in the rich text body. Only create a custom block when the needed structure or behavior cannot be represented by built-in blocks plus a host-owned override or styling.
29
36
 
30
37
  ### 2. List Existing Blocks
31
38
 
@@ -2,6 +2,12 @@
2
2
 
3
3
  Add layout variants and custom fields to built-in system blocks without creating entirely new custom blocks.
4
4
 
5
+ In a fully headless generated site, the CMS kind remains a built-in semantic
6
+ kind such as `block.hero`; the host owns how that kind is rendered. Register
7
+ the host component for the kind in `src/lib/block-overrides.tsx` after adding
8
+ the extension. An extension changes the content contract, not the rendering
9
+ authority.
10
+
5
11
  ## When to Use Block Extensions
6
12
 
7
13
  **Use block extensions when:**
@@ -24,7 +30,7 @@ See `.riverbank/docs/block-types.md` for which system blocks support variants an
24
30
  ## Prerequisites
25
31
 
26
32
  - `riverbank.config.ts` exists
27
- - Block overrides file set up (e.g., `src/lib/block-overrides.tsx`)
33
+ - Host override registry set up (e.g., `src/lib/block-overrides.tsx`)
28
34
  - Understanding of the system block you're extending
29
35
 
30
36
  ## Steps
@@ -103,18 +109,23 @@ export function CustomHero({ content, ...props }: BlockOverrideProps<ExtendedHer
103
109
  }
104
110
  ```
105
111
 
106
- ### Step 3: Register in blockOverrides
112
+ ### Step 3: Register in `blockOverrides`
107
113
 
108
- Add your override component to the block overrides:
114
+ Add your host-owned override component to the typed registration map. The CMS
115
+ content continues to use `block.hero`; `hero` is the corresponding short
116
+ registry key.
109
117
 
110
118
  ```typescript
111
119
  // src/lib/block-overrides.tsx
112
- import type { BlockOverrides } from '@riverbankcms/sdk/rendering';
120
+ import {
121
+ defineBlockOverrideRegistrations,
122
+ type BlockOverrideRegistrations,
123
+ } from '@riverbankcms/sdk/rendering';
113
124
  import { CustomHero } from '@/components/CustomHero';
114
125
 
115
- export const blockOverrides: BlockOverrides = {
126
+ export const blockOverrides = defineBlockOverrideRegistrations({
116
127
  hero: CustomHero,
117
- };
128
+ }) satisfies BlockOverrideRegistrations;
118
129
  ```
119
130
 
120
131
  ### Step 4: Push Config
@@ -323,6 +334,11 @@ blockFieldExtensions: {
323
334
  **Override component:**
324
335
 
325
336
  ```typescript
337
+ import {
338
+ defineBlockOverrideRegistrations,
339
+ type BlockOverrideRegistrations,
340
+ } from '@riverbankcms/sdk/rendering';
341
+
326
342
  type ExtendedHeroContent = HeroContent & {
327
343
  layout?: 'default' | 'topography';
328
344
  topographyColor?: 'teal' | 'green' | 'tan' | 'brown';
@@ -338,9 +354,9 @@ const HeroOverride = (props: BlockOverrideProps<ExtendedHeroContent>) => {
338
354
  return <CustomHeroWithCanvas {...props} />;
339
355
  };
340
356
 
341
- export const blockOverrides: BlockOverrides = {
357
+ export const blockOverrides = defineBlockOverrideRegistrations({
342
358
  hero: HeroOverride,
343
- };
359
+ }) satisfies BlockOverrideRegistrations;
344
360
  ```
345
361
 
346
362
  ## Troubleshooting
@@ -359,7 +375,7 @@ export const blockOverrides: BlockOverrides = {
359
375
 
360
376
  ### Override component not rendering
361
377
 
362
- - Verify the override is registered in `blockOverrides`
378
+ - Verify the host override is registered in `blockOverrides`
363
379
  - Check the key matches the block name (e.g., `hero` for `block.hero`)
364
380
  - Ensure the component is exported correctly
365
381
 
@@ -2,6 +2,11 @@
2
2
 
3
3
  Create a site-specific block with custom fields and rendering when system blocks don't meet your needs.
4
4
 
5
+ Generated sites are fully headless by default: custom block kinds are content
6
+ semantics, while the host owns their markup and styles. Register each custom
7
+ kind in the host override registry; missing registrations fail visibly instead
8
+ of falling back to a CMS renderer.
9
+
5
10
  ## When to Use Custom Blocks
6
11
 
7
12
  Before creating a custom block, check `block-types.md` and the site `schema.md`. Many content needs are already covered by system blocks:
@@ -26,7 +31,7 @@ Do not create a custom block just to get “a rich text area with different spac
26
31
  ## Prerequisites
27
32
 
28
33
  - `riverbank.config.ts` exists
29
- - Block overrides file set up (e.g., `src/lib/block-overrides.tsx`)
34
+ - Host override registry set up (e.g., `src/lib/block-overrides.tsx`)
30
35
  - Understanding of the content structure you need
31
36
 
32
37
  ## Steps
@@ -163,18 +168,40 @@ export function OurFlowBlock({ content }: BlockOverrideProps<OurFlowContent>) {
163
168
  }
164
169
  ```
165
170
 
166
- ### Step 4: Register in blockOverrides
171
+ ### Step 4: Register in `blockOverrides`
167
172
 
168
- Add your component to the block overrides:
173
+ Register the host component with the typed registrations helper. The CMS
174
+ content keeps the semantic `custom.our-flow` kind.
169
175
 
170
176
  ```typescript
171
177
  // src/lib/block-overrides.tsx
172
- import type { BlockOverrides } from '@riverbankcms/sdk/rendering';
178
+ import {
179
+ defineBlockOverrideRegistrations,
180
+ type BlockOverrideRegistrations,
181
+ } from '@riverbankcms/sdk/rendering';
173
182
  import { OurFlowBlock } from '@/components/OurFlowBlock';
174
183
 
175
- export const blockOverrides: BlockOverrides = {
184
+ export const blockOverrides = defineBlockOverrideRegistrations({
176
185
  'custom.our-flow': OurFlowBlock,
177
- };
186
+ }) satisfies BlockOverrideRegistrations;
187
+ ```
188
+
189
+ For client-heavy or rarely used blocks, wrap the dynamic import with
190
+ `defineLazyBlockOverride` so the component is loaded only when its kind is
191
+ rendered:
192
+
193
+ ```typescript
194
+ import {
195
+ defineBlockOverrideRegistrations,
196
+ defineLazyBlockOverride,
197
+ type BlockOverrideRegistrations,
198
+ } from '@riverbankcms/sdk/rendering';
199
+
200
+ export const blockOverrides = defineBlockOverrideRegistrations({
201
+ 'custom.our-flow': defineLazyBlockOverride(async () =>
202
+ import('@/components/OurFlowBlock').then((module) => module.OurFlowBlock),
203
+ ),
204
+ }) satisfies BlockOverrideRegistrations;
178
205
  ```
179
206
 
180
207
  ### Step 5: Push Config
@@ -453,8 +480,8 @@ Pricing cards with nested features and conditional button actions:
453
480
 
454
481
  ### Component not rendering
455
482
 
456
- - Verify override is registered with exact block ID
457
- - Check the key in `blockOverrides` matches (e.g., `'custom.our-flow'`)
483
+ - Verify the host override is registered with the exact block ID
484
+ - Check the key in `blockOverrides` matches the semantic kind (e.g., `'custom.our-flow'`)
458
485
  - Ensure component is exported correctly
459
486
 
460
487
  ### Repeater items not working
@@ -7,12 +7,13 @@ Configure a Riverbank SDK Next.js App Router site to prerender published routes,
7
7
  ## Prerequisites
8
8
 
9
9
  - A Riverbank SDK site using `createCatchAllPage()` in `src/app/[[...slug]]/page.tsx`.
10
- - A production content API key in `RIVERBANK_API_KEY`.
11
- - `RIVERBANK_SITE_ID` set to this site's Riverbank site ID.
12
- - `NEXT_PUBLIC_DASHBOARD_URL` set to the dashboard origin, for example `https://cms.example.com`.
10
+ - A configured `getRiverbankClient()` helper and `siteId` in `riverbank.config.ts`.
13
11
  - `RIVERBANK_WEBHOOK_SECRET` set from the CMS-generated Riverbank webhook secret.
14
12
 
15
- Preview deployments should also set `RIVERBANK_PREVIEW_MODE=true` so draft previews stay dynamic instead of serving stale ISR HTML.
13
+ Sites scaffolded with preview support should set `RIVERBANK_PREVIEW_MODE=true` in
14
+ preview deployments. The SDK preview integration reads the request-bound preview
15
+ session and opts those requests into dynamic rendering; the literal ISR interval
16
+ remains unchanged.
16
17
 
17
18
  ## Configure the Catch-All Route
18
19
 
@@ -21,21 +22,26 @@ In `src/app/[[...slug]]/page.tsx`:
21
22
  ```tsx
22
23
  import {
23
24
  createCatchAllPage,
24
- generateAllStaticParams,
25
- getISRConfig,
25
+ createGenerateStaticParams,
26
26
  } from '@riverbankcms/sdk/next'
27
27
 
28
28
  import config from '@/riverbank.config'
29
29
  import { blockOverrides } from '@/lib/block-overrides'
30
30
  import { getRiverbankClient } from '@/lib/riverbank-client'
31
31
 
32
- export const revalidate = getISRConfig().revalidate
32
+ // Next.js route config values must remain statically analyzable literals.
33
+ export const revalidate = 300
33
34
  export const dynamicParams = true
34
- export { generateAllStaticParams as generateStaticParams }
35
+ export const generateStaticParams = createGenerateStaticParams({
36
+ getClient: getRiverbankClient,
37
+ siteId: config.siteId,
38
+ failureMode: 'throw',
39
+ })
35
40
 
36
41
  const { Page, generateMetadata } = createCatchAllPage({
37
42
  getClient: getRiverbankClient,
38
43
  config,
44
+ searchParamsMode: 'ignore',
39
45
  blockOverrides,
40
46
  })
41
47
 
@@ -43,7 +49,18 @@ export default Page
43
49
  export { generateMetadata }
44
50
  ```
45
51
 
46
- `generateAllStaticParams()` discovers published pages and routable entries at build time. It reads:
52
+ `searchParamsMode: 'ignore'` is required for this ISR shape because reading the
53
+ Next.js page `searchParams` prop opts the route into dynamic rendering. Use the
54
+ default `dynamic` mode only when server-rendered themes, data loaders, redirects,
55
+ or metadata genuinely depend on URL query parameters.
56
+
57
+ `createGenerateStaticParams()` uses the site's configured client to discover
58
+ published pages and routable entries at build time. The example uses strict
59
+ failure handling so production CI cannot silently publish an incomplete route
60
+ set.
61
+
62
+ If a site instead uses the SDK's generic environment variable names,
63
+ `generateAllStaticParams()` is available. It reads:
47
64
 
48
65
  - `RIVERBANK_API_KEY`
49
66
  - `RIVERBANK_SITE_ID`
@@ -59,21 +76,6 @@ export function generateStaticParams() {
59
76
  }
60
77
  ```
61
78
 
62
- If your site already centralizes the SDK client, use the explicit factory:
63
-
64
- ```tsx
65
- import { createGenerateStaticParams } from '@riverbankcms/sdk/next'
66
-
67
- import config from '@/riverbank.config'
68
- import { getRiverbankClient } from '@/lib/riverbank-client'
69
-
70
- export const generateStaticParams = createGenerateStaticParams({
71
- getClient: getRiverbankClient,
72
- siteId: config.siteId,
73
- failureMode: 'throw',
74
- })
75
- ```
76
-
77
79
  ## Add the Revalidation Route
78
80
 
79
81
  Create `src/app/api/revalidate/route.ts`:
@@ -4,6 +4,11 @@
4
4
 
5
5
  Enable dashboard preview editing for an SDK-hosted Next.js site. Preview mode lets the dashboard open the external site with a short-lived editor session, render draft content, and show the Riverbank preview shell.
6
6
 
7
+ The generated fully headless site keeps `SITE_RENDERING` as the rendering
8
+ authority in both the live catch-all route and this preview path. Preserve its
9
+ `overrides-only`/`runtime: none` contract when adding preview behavior: preview
10
+ changes the content/session source, not who owns block markup and styles.
11
+
7
12
  ## Prerequisites
8
13
 
9
14
  - A Riverbank SDK site using the Next.js App Router.
@@ -111,13 +116,20 @@ import {
111
116
  } from '@riverbankcms/sdk/preview-next/client'
112
117
 
113
118
  import { blockOverrides } from '@/lib/block-overrides'
119
+ import { SITE_RENDERING } from '@/lib/rendering'
114
120
 
115
121
  export function SdkPreviewModeClient(
116
122
  props: SdkPreviewModePropsWithoutBlockOverrides
117
123
  ) {
124
+ const { themeRuntime: _themeRuntime, ...previewProps } = props
125
+
118
126
  return (
119
127
  <PreviewQueryProvider>
120
- <SdkPreviewMode {...props} blockOverrides={blockOverrides} />
128
+ <SdkPreviewMode
129
+ {...previewProps}
130
+ rendering={SITE_RENDERING}
131
+ blockOverrides={blockOverrides}
132
+ />
121
133
  </PreviewQueryProvider>
122
134
  )
123
135
  }
@@ -134,11 +146,13 @@ import { createPreviewModeBeforeRender } from '@riverbankcms/sdk/preview-next/be
134
146
  import config from '../../../riverbank.config'
135
147
  import { blockOverrides } from '@/lib/block-overrides'
136
148
  import { getRiverbankClient } from '@/lib/riverbank-client'
149
+ import { SITE_RENDERING } from '@/lib/rendering'
137
150
  import { SdkPreviewModeClient } from '@/components/preview/SdkPreviewModeClient'
138
151
 
139
152
  const { Page, generateMetadata } = createCatchAllPage({
140
153
  getClient: getRiverbankClient,
141
154
  config,
155
+ rendering: SITE_RENDERING,
142
156
  blockOverrides,
143
157
  beforeRender: createPreviewModeBeforeRender({
144
158
  getClient: getRiverbankClient,
@@ -151,9 +165,16 @@ export default Page
151
165
  export { generateMetadata }
152
166
  ```
153
167
 
154
- ## Custom Header/Footer Chrome
168
+ ## Optional Custom Preview Chrome
155
169
 
156
- If your site replaces the SDK header or footer, pass that chrome into `PreviewLayoutProvider` only when there is an editable preview session. `RIVERBANK_PREVIEW_MODE=true` makes the route preview-capable, but `getPreviewSession()` tells you whether the current request actually has preview cookies. Regular visitors without preview cookies should keep the normal SDK `Layout`.
170
+ Fully headless generated sites keep a plain host-owned root layout. If the host
171
+ replaces the preview header or footer, pass that chrome into
172
+ `PreviewLayoutProvider` only when there is an editable preview session.
173
+ `RIVERBANK_PREVIEW_MODE=true` makes the route preview-capable, but
174
+ `getPreviewSession()` tells you whether the current request actually has
175
+ preview cookies. Regular visitors without preview cookies should keep the
176
+ site's normal layout. The `Layout` example below is only for a site that
177
+ intentionally opts into themed SDK rendering.
157
178
 
158
179
  In `src/app/layout.tsx`:
159
180
 
@@ -212,9 +233,12 @@ export default async function RootLayout({
212
233
 
213
234
  If your custom chrome needs CMS navigation data, build the `headerNode` and `footerNode` from the same data used by your normal layout before branching. Calling `getPreviewSession()` in the root layout reads cookies, so the layout becomes dynamic.
214
235
 
215
- ## Import Preview CSS
236
+ ## Import Preview Editor Chrome CSS
216
237
 
217
- For Tailwind sites, add this to `src/app/globals.css`:
238
+ Preview-enabled sites need the SDK preview editor chrome styles even when they
239
+ are fully headless. These imports provide the preview shell and editor chrome;
240
+ they are not the CMS theme or built-in block styles. A Tailwind site can add
241
+ this to `src/app/globals.css`:
218
242
 
219
243
  ```css
220
244
  @import 'tailwindcss';
@@ -222,7 +246,7 @@ For Tailwind sites, add this to `src/app/globals.css`:
222
246
  @import '@riverbankcms/sdk/preview-next/chrome.css';
223
247
  ```
224
248
 
225
- For non-Tailwind sites, use the legacy single import:
249
+ For non-Tailwind sites, use the legacy single preview-chrome import:
226
250
 
227
251
  ```css
228
252
  @import '@riverbankcms/sdk/preview-next/styles.css';