Back to Plate

Forced Layout

content/docs/(plugins)/(functionality)/(utils)/forced-layout.mdx

53.0.84.7 KB
Original Source

Forced 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 when the requirement is "the document always ends with a paragraph."

<PackageInfo>

Features

  • Path-indexed normalization rules.
  • strictType for rewriting an existing node to a required type.
  • type for inserting a missing node without rewriting an existing node.
  • Root-only normalization pass.
  • Automatic block creation through editor.api.create.block.
  • onError callback when insertion fails.
  • Conditional enabling through normal plugin enabled configuration.
</PackageInfo>

Fast Path

Add NormalizeTypesPlugin when specific paths must exist or hold a specific block type.

tsx
import { KEYS, NormalizeTypesPlugin } from 'platejs';
import { createPlateEditor } from 'platejs/react';

export const editor = createPlateEditor({
  plugins: [
    NormalizeTypesPlugin.configure({
      options: {
        rules: [
          { path: [0], strictType: KEYS.h1 },
          { path: [1], type: KEYS.p },
        ],
      },
    }),
  ],
});

This keeps the first block as an H1 and inserts a paragraph at path [1] when that node is missing.

Ownership

LayerOwnerWhat It Does
NormalizeTypesPluginplatejs / @platejs/utilsStores rules and onError, then overrides normalization.
withNormalizeTypes@platejs/utilsRuns the path rules during root normalization.
NodeApi.get(editor, path)@platejs/slateReads the node at the configured path.
editor.api.create.blockCore editor APICreates inserted or replacement block props.
Playground demoRegistry exampleEnables a first-block H1 rule when the playground id is forced-layout.

There is no ForcedLayoutPlugin and no forced-layout-kit. The public plugin is NormalizeTypesPlugin.

Rule Semantics

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.

Rule ShapeExisting NodeMissing Node
{ 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 }).
{ path, type }Plate leaves the node alone.Plate inserts editor.api.create.block({ type }).

Use strictType for required slots. Use type for optional slots that should be filled only when empty.

Error Handling

If inserting a missing node fails, withNormalizeTypes calls onError(error) and falls through to the editor's normal normalizeNode.

tsx
import { NormalizeTypesPlugin } from 'platejs';

export const requiredTitle = NormalizeTypesPlugin.configure({
  options: {
    onError: (error) => {
      console.error(error);
    },
    rules: [{ path: [0], strictType: 'h1' }],
  },
});

Keep onError small. A normalization callback should report or collect the failure, not mutate the same path again.

Choosing The Right Utility

NeedUse
First block must be a titleNormalizeTypesPlugin with strictType.
A missing slot should be insertedNormalizeTypesPlugin with type.
Editor may only contain one root blockSingle Block.
Editor must end with a paragraphTrailing Block.
Pressing Enter should exit or reset a blockPlugin Rules.

Forced layout is for absolute paths. It is not a schema engine for every possible nested node shape.

Playground Toggle

The registry playground demonstrates this pattern by enabling the plugin only for the forced-layout example id.

tsx
NormalizeTypesPlugin.configure({
  enabled: id === 'forced-layout',
  options: {
    rules: [{ path: [0], strictType: 'h1' }],
  },
});

That example keeps the first playground block as an H1 while leaving the rest of the editor to normal Plate behavior.

API Reference

APIPackageUse
NormalizeTypesPluginplatejs / @platejs/utilsPath-based type normalization plugin.
NormalizeTypesConfig.options.rules@platejs/utilsOrdered list of path rules. Defaults to [].
Rule.path@platejs/utilsSlate Path where the rule applies.
Rule.strictType@platejs/utilsRequired type for an existing or missing node.
Rule.type@platejs/utilsType for a missing node only.
NormalizeTypesConfig.options.onError@platejs/utilsCalled when inserting a missing node throws.