{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "forced-layout-docs",
  "title": "Forced Layout",
  "description": "Path-based normalization for required document structure.",
  "files": [
    {
      "path": "../../content/docs/(plugins)/(functionality)/(utils)/forced-layout.mdx",
      "content": "---\ntitle: Forced Layout\ndescription: Path-based normalization for required document structure.\ndocs:\n  - route: /docs/trailing-block\n    title: Trailing Block\n  - route: /docs/single-block\n    title: Single Block\n  - route: /docs/plugin-rules\n    title: Plugin Rules\n---\n\nForced Layout is the `NormalizeTypesPlugin` pattern for pinning document positions to required node types. Use it for fixed slots such as \"the first block is an H1.\" Use [Trailing Block](/docs/trailing-block) when the requirement is \"the document always ends with a paragraph.\"\n\n<PackageInfo>\n\n## Features\n\n- Path-indexed normalization rules.\n- `strictType` for rewriting an existing node to a required type.\n- `type` for inserting a missing node without rewriting an existing node.\n- Root-only normalization pass.\n- Automatic block creation through `editor.api.create.block`.\n- `onError` callback when insertion fails.\n- Conditional enabling through normal plugin `enabled` configuration.\n\n</PackageInfo>\n\n## Fast Path\n\nAdd `NormalizeTypesPlugin` when specific paths must exist or hold a specific block type.\n\n```tsx\nimport { KEYS, NormalizeTypesPlugin } from 'platejs';\nimport { createPlateEditor } from 'platejs/react';\n\nexport const editor = createPlateEditor({\n  plugins: [\n    NormalizeTypesPlugin.configure({\n      options: {\n        rules: [\n          { path: [0], strictType: KEYS.h1 },\n          { path: [1], type: KEYS.p },\n        ],\n      },\n    }),\n  ],\n});\n```\n\nThis keeps the first block as an H1 and inserts a paragraph at path `[1]` when that node is missing.\n\n## Ownership\n\n| Layer | Owner | What It Does |\n|-------|-------|--------------|\n| `NormalizeTypesPlugin` | `platejs` / `@platejs/utils` | Stores `rules` and `onError`, then overrides normalization. |\n| `withNormalizeTypes` | `@platejs/utils` | Runs the path rules during root normalization. |\n| `NodeApi.get(editor, path)` | `@platejs/slate` | Reads the node at the configured path. |\n| `editor.api.create.block` | Core editor API | Creates inserted or replacement block props. |\n| Playground demo | Registry example | Enables a first-block H1 rule when the playground id is `forced-layout`. |\n\nThere is no `ForcedLayoutPlugin` and no `forced-layout-kit`. The public plugin is `NormalizeTypesPlugin`.\n\n## Rule Semantics\n\n`NormalizeTypesPlugin` runs only when the root editor node normalizes. It checks rules in order and stops the current normalization pass after the first rule that changes the document.\n\n| Rule Shape | Existing Node | Missing Node |\n|------------|---------------|--------------|\n| `{ path, strictType }` | If the node is an element with a different type, Plate sets its block props to `strictType` and preserves children. | Plate inserts `editor.api.create.block({ type: strictType })`. |\n| `{ path, type }` | Plate leaves the node alone. | Plate inserts `editor.api.create.block({ type })`. |\n\nUse `strictType` for required slots. Use `type` for optional slots that should be filled only when empty.\n\n## Error Handling\n\nIf inserting a missing node fails, `withNormalizeTypes` calls `onError(error)` and falls through to the editor's normal `normalizeNode`.\n\n```tsx\nimport { NormalizeTypesPlugin } from 'platejs';\n\nexport const requiredTitle = NormalizeTypesPlugin.configure({\n  options: {\n    onError: (error) => {\n      console.error(error);\n    },\n    rules: [{ path: [0], strictType: 'h1' }],\n  },\n});\n```\n\nKeep `onError` small. A normalization callback should report or collect the failure, not mutate the same path again.\n\n## Choosing The Right Utility\n\n| Need | Use |\n|------|-----|\n| First block must be a title | `NormalizeTypesPlugin` with `strictType`. |\n| A missing slot should be inserted | `NormalizeTypesPlugin` with `type`. |\n| Editor may only contain one root block | [Single Block](/docs/single-block). |\n| Editor must end with a paragraph | [Trailing Block](/docs/trailing-block). |\n| Pressing Enter should exit or reset a block | [Plugin Rules](/docs/plugin-rules). |\n\nForced layout is for absolute paths. It is not a schema engine for every possible nested node shape.\n\n## Playground Toggle\n\nThe registry playground demonstrates this pattern by enabling the plugin only for the `forced-layout` example id.\n\n```tsx\nNormalizeTypesPlugin.configure({\n  enabled: id === 'forced-layout',\n  options: {\n    rules: [{ path: [0], strictType: 'h1' }],\n  },\n});\n```\n\nThat example keeps the first playground block as an H1 while leaving the rest of the editor to normal Plate behavior.\n\n## API Reference\n\n| API | Package | Use |\n|-----|---------|-----|\n| `NormalizeTypesPlugin` | `platejs` / `@platejs/utils` | Path-based type normalization plugin. |\n| `NormalizeTypesConfig.options.rules` | `@platejs/utils` | Ordered list of path rules. Defaults to `[]`. |\n| `Rule.path` | `@platejs/utils` | Slate `Path` where the rule applies. |\n| `Rule.strictType` | `@platejs/utils` | Required type for an existing or missing node. |\n| `Rule.type` | `@platejs/utils` | Type for a missing node only. |\n| `NormalizeTypesConfig.options.onError` | `@platejs/utils` | Called when inserting a missing node throws. |\n",
      "type": "registry:file",
      "target": "content/docs/plate/(plugins)/(functionality)/(utils)/forced-layout.mdx"
    }
  ],
  "type": "registry:file"
}