riverbankcms 0.2.0 → 0.4.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.
@@ -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
@@ -139,7 +203,6 @@ riverbankcms pull --output ./src/content
139
203
  | `--yes` | Skip confirmation prompt |
140
204
  | `--local-media` | Download media referenced by identifiers to content/media |
141
205
  | `--sync-media` | Sync media files between environments |
142
- | `--overwrite-media` | When using `--sync-media`, overwrite target media on checksum mismatch |
143
206
  | `--with-theme` | Also pull theme when pulling all content |
144
207
  | `--name <name>` | Theme name for `pull theme` / `pull --with-theme` (default: current) |
145
208
  | `--no-meta` | Skip writing `.meta/` and media manifest files (content files still written) |
@@ -201,14 +264,12 @@ If unrelated local files would be included by a broad scope, stage a temporary c
201
264
  | `--dry-run` | Show changes without applying |
202
265
  | `--yes` | Skip confirmation (required for `--env=remote` or `--env=both`) |
203
266
  | `--force` | Push even if remote is newer (skip stale check) |
204
- | `--force-update-asset` | Replace CMS media when identifiers conflict (local media sync only) |
205
267
  | `--allow-truncated` | Push even if remote content was truncated (may cause incomplete sync) |
206
268
  | `--diff-detail <mode>` | Diff verbosity carried in the `--json` envelope: `summary` (default) or `full` |
207
269
  | `--json-diff [mode]` | **[Deprecated]** Alias of `--diff-detail`; folded into the `--json` envelope (warns on stderr) |
208
270
  | `--with-config` | Push SDK schema/config before dependent file content |
209
271
  | `--with-theme` | Also push theme when pushing all content |
210
272
  | `--sync-media` | Sync media files from local to remote environment |
211
- | `--overwrite-media` | When using `--sync-media`, overwrite target media on checksum mismatch |
212
273
  | `--merge-remote` | Plan an entries/pages three-way merge for `--env=remote` that preserves remote edits |
213
274
  | `--delete-orphaned` | Delete CMS entries not present in local files |
214
275
  | `--auto-pull-stale` | Automatically pull stale content and retry push |
@@ -356,6 +417,42 @@ export default defineConfig({
356
417
 
357
418
  **Status Sync**: Push automatically syncs draft/published status based on the `status` field in your local content files. If local content is "published" but remote is "draft", push will publish it. If local is "draft" but remote is "published", push will unpublish it.
358
419
 
420
+ ### settings booking configure
421
+
422
+ Configure the site's booking vertical and selected presets through the
423
+ purpose-built settings operation:
424
+
425
+ ```bash
426
+ # Ask the server for an authoritative preview; do not write
427
+ riverbankcms settings booking configure \
428
+ --vertical fitness_pt \
429
+ --preset fitness_pt.group_training \
430
+ --dry-run --json
431
+
432
+ # Apply locally, or target production with --env=remote --yes
433
+ riverbankcms settings booking configure \
434
+ --vertical fitness_pt \
435
+ --preset fitness_pt.group_training \
436
+ --yes
437
+ ```
438
+
439
+ Options:
440
+
441
+ | Option | Description |
442
+ | --- | --- |
443
+ | `--vertical <vertical>` | Required canonical booking vertical identifier |
444
+ | `--preset <preset>` | Required preset identifier; repeat for multiple presets |
445
+ | `--dry-run` | Show the server-authoritative preview without writing |
446
+ | `--yes` | Skip confirmation; required for JSON/non-interactive writes |
447
+
448
+ The command validates the selection locally, previews on the server, and
449
+ revalidates immediately before applying. `--env=both` preflights both targets,
450
+ then applies local before remote. Changing the vertical does not rewrite
451
+ existing offerings or their stored presets. The declarative equivalent is
452
+ `verticalConfig` in `content/settings.json` followed by `riverbankcms push
453
+ settings`; `riverbankcms pull settings` writes the canonical configuration
454
+ back to that file.
455
+
359
456
  ### push-config
360
457
 
361
458
  Push SDK configuration to the CMS dashboard.
@@ -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
@@ -223,6 +232,42 @@ Notes:
223
232
  - `riverbankcms pull settings` warns and omits `logoIdentifier` / `faviconIdentifier` if the current CMS branding asset has no media identifier yet
224
233
  - Dashboard branding edits and CLI pushes both update the same site settings row, so treat them as last-write-wins and pull before pushing after dashboard-side edits
225
234
 
235
+ ### Configure the booking vertical
236
+
237
+ Booking vertical selection is a purpose-built settings operation rather than a
238
+ generic `settings.update` field. The CLI validates the vertical and every
239
+ preset against the canonical catalog before contacting the CMS:
240
+
241
+ ```bash
242
+ # Preview the server-authoritative result without changing the site
243
+ riverbankcms settings booking configure \
244
+ --vertical fitness_pt \
245
+ --preset fitness_pt.group_training \
246
+ --dry-run --json
247
+
248
+ # Apply the same configuration to the local CMS
249
+ riverbankcms settings booking configure \
250
+ --vertical fitness_pt \
251
+ --preset fitness_pt.group_training \
252
+ --yes
253
+ ```
254
+
255
+ Repeat `--preset` to select more than one preset. Use `--env=remote` or
256
+ `--env=both --yes` to target the other project environment. The command asks
257
+ the server for a preview before applying and revalidates that preview
258
+ immediately before the write; `--dry-run` reports that authoritative preview
259
+ without writing anything. JSON and other non-interactive writes require
260
+ `--yes`.
261
+
262
+ Changing the vertical does not rewrite existing offerings. Their stored
263
+ presets remain unchanged; this operation only changes the site-level booking
264
+ configuration.
265
+
266
+ The declarative equivalent is to add `verticalConfig` to
267
+ `content/settings.json` and run `riverbankcms push settings` (or the relevant
268
+ scope of `riverbankcms push`). Pulling settings writes the canonical vertical
269
+ and preset order back to that file.
270
+
226
271
  ---
227
272
 
228
273
  ## Draft vs Published
@@ -423,14 +468,12 @@ Use this when you explicitly want to copy media bytes between environments witho
423
468
  # Remote storage -> local storage (identifier + checksum based)
424
469
  riverbankcms pull --env=remote --sync-media --yes
425
470
 
426
- # Fail-safe by default: checksum mismatches abort unless you explicitly overwrite
427
- riverbankcms pull --env=remote --sync-media --overwrite-media --yes
471
+ # Checksum mismatches abort; delete the target asset first or use a new identifier
428
472
  ```
429
473
 
430
474
  Notes:
431
475
 
432
- - `--sync-media` is **identifier + checksum** based. If the target already has an identifier with different bytes, the command fails unless you pass `--overwrite-media`.
433
- - When pulling from remote, `--sync-media` may need to **write to the source CMS** to backfill missing identifiers, so remote sync requires `--yes`.
476
+ - `--sync-media` is **identifier + checksum** based. If the target already has an identifier with different bytes, the command fails without modifying either asset.
434
477
  - Local media bytes live under `content/media/`, which is treated as cache/migration working data by default in generated SDK sites.
435
478
 
436
479
  #### Promote local files → production CMS
@@ -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
@@ -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';