Heading

PreviousNext

H1 through H6 block elements with shortcuts, input rules, and static renderers.

Heading adds six block element types, h1 through h6, for document structure. Each level is its own plugin with the same block behavior, HTML parser, toggle transform, and optional Markdown shorthand rule. This page covers registry setup, manual setup, value shape, shortcuts, input rules, and Markdown serialization.

Loading…

Features

  • Block h1 through h6 elements.
  • Individual H1Plugin through H6Plugin exports.
  • Grouping HeadingPlugin and BaseHeadingPlugin with configurable levels.
  • Bound editor.tf.h1.toggle() through editor.tf.h6.toggle() transforms.
  • # through ###### input rules with HeadingRules.markdown().
  • mod+alt+1 through mod+alt+6 shortcuts in the registry kit.
  • Editable and static registry heading components.

Fast Path

Add Basic Blocks

BasicBlocksKit installs paragraph, H1-H6, blockquote, and horizontal rule plugins with Plate UI components. For headings, it configures every level with HeadingRules.markdown(), H*Element, a reset-on-empty break rule, and mod+alt+<level> shortcuts.

'use client';
 
import {
  BlockquoteRules,
  HeadingRules,
  HorizontalRuleRules,
} from '@platejs/basic-nodes';
import {
  BlockquotePlugin,
  H1Plugin,
  H2Plugin,
  H3Plugin,
  H4Plugin,
  H5Plugin,
  H6Plugin,
  HorizontalRulePlugin,
} from '@platejs/basic-nodes/react';
import { ParagraphPlugin } from 'platejs/react';
 
import { BlockquoteElement } from '@/components/ui/blockquote-node';
import {
  H1Element,
  H2Element,
  H3Element,
  H4Element,
  H5Element,
  H6Element,
} from '@/components/ui/heading-node';
import { HrElement } from '@/components/ui/hr-node';
import { ParagraphElement } from '@/components/ui/paragraph-node';
 
export const BasicBlocksKit = [
  ParagraphPlugin.withComponent(ParagraphElement),
  H1Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H1Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+1' } },
  }),
  H2Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H2Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+2' } },
  }),
  H3Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H3Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+3' } },
  }),
  H4Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H4Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+4' } },
  }),
  H5Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H5Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+5' } },
  }),
  H6Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H6Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+6' } },
  }),
  BlockquotePlugin.configure({
    inputRules: [BlockquoteRules.markdown()],
    node: { component: BlockquoteElement },
    shortcuts: { toggle: { keys: 'mod+shift+period' } },
  }),
  HorizontalRulePlugin.configure({
    inputRules: [
      HorizontalRuleRules.markdown({ variant: '-' }),
      HorizontalRuleRules.markdown({ variant: '_' }),
    ],
    node: {
      component: HrElement,
    },
  }),
];
'use client';
 
import {
  BlockquoteRules,
  HeadingRules,
  HorizontalRuleRules,
} from '@platejs/basic-nodes';
import {
  BlockquotePlugin,
  H1Plugin,
  H2Plugin,
  H3Plugin,
  H4Plugin,
  H5Plugin,
  H6Plugin,
  HorizontalRulePlugin,
} from '@platejs/basic-nodes/react';
import { ParagraphPlugin } from 'platejs/react';
 
import { BlockquoteElement } from '@/components/ui/blockquote-node';
import {
  H1Element,
  H2Element,
  H3Element,
  H4Element,
  H5Element,
  H6Element,
} from '@/components/ui/heading-node';
import { HrElement } from '@/components/ui/hr-node';
import { ParagraphElement } from '@/components/ui/paragraph-node';
 
export const BasicBlocksKit = [
  ParagraphPlugin.withComponent(ParagraphElement),
  H1Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H1Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+1' } },
  }),
  H2Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H2Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+2' } },
  }),
  H3Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H3Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+3' } },
  }),
  H4Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H4Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+4' } },
  }),
  H5Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H5Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+5' } },
  }),
  H6Plugin.configure({
    inputRules: [HeadingRules.markdown()],
    node: {
      component: H6Element,
    },
    rules: {
      break: { empty: 'reset' },
    },
    shortcuts: { toggle: { keys: 'mod+alt+6' } },
  }),
  BlockquotePlugin.configure({
    inputRules: [BlockquoteRules.markdown()],
    node: { component: BlockquoteElement },
    shortcuts: { toggle: { keys: 'mod+shift+period' } },
  }),
  HorizontalRulePlugin.configure({
    inputRules: [
      HorizontalRuleRules.markdown({ variant: '-' }),
      HorizontalRuleRules.markdown({ variant: '_' }),
    ],
    node: {
      component: HrElement,
    },
  }),
];
import { createPlateEditor } from 'platejs/react';
 
import { BasicBlocksKit } from '@/components/editor/plugins/basic-blocks-kit';
 
export const editor = createPlateEditor({
  plugins: BasicBlocksKit,
});
import { createPlateEditor } from 'platejs/react';
 
import { BasicBlocksKit } from '@/components/editor/plugins/basic-blocks-kit';
 
export const editor = createPlateEditor({
  plugins: BasicBlocksKit,
});

Render Headings

heading-node exports H1Element through H6Element. Each component renders a PlateElement with the matching heading tag and navigation-highlight styles.

'use client';
 
import * as React from 'react';
 
import type { PlateElementProps } from 'platejs/react';
 
import { type VariantProps, cva } from 'class-variance-authority';
import { PlateElement } from 'platejs/react';
 
const headingVariants = cva(
  'relative mb-1 data-[nav-target=true]:rounded-md data-[nav-target=true]:bg-(--color-highlight)',
  {
    variants: {
      variant: {
        h1: 'mt-[1.6em] pb-1 font-bold font-heading text-4xl',
        h2: 'mt-[1.4em] pb-px font-heading font-semibold text-2xl tracking-tight',
        h3: 'mt-[1em] pb-px font-heading font-semibold text-xl tracking-tight',
        h4: 'mt-[0.75em] font-heading font-semibold text-lg tracking-tight',
        h5: 'mt-[0.75em] font-semibold text-lg tracking-tight',
        h6: 'mt-[0.75em] font-semibold text-base tracking-tight',
      },
    },
  }
);
 
export function HeadingElement({
  variant = 'h1',
  ...props
}: PlateElementProps & VariantProps<typeof headingVariants>) {
  return (
    <PlateElement
      as={variant!}
      className={headingVariants({ variant })}
      {...props}
    >
      {props.children}
    </PlateElement>
  );
}
 
export function H1Element(props: PlateElementProps) {
  return <HeadingElement variant="h1" {...props} />;
}
 
export function H2Element(props: PlateElementProps) {
  return <HeadingElement variant="h2" {...props} />;
}
 
export function H3Element(props: PlateElementProps) {
  return <HeadingElement variant="h3" {...props} />;
}
 
export function H4Element(props: PlateElementProps) {
  return <HeadingElement variant="h4" {...props} />;
}
 
export function H5Element(props: PlateElementProps) {
  return <HeadingElement variant="h5" {...props} />;
}
 
export function H6Element(props: PlateElementProps) {
  return <HeadingElement variant="h6" {...props} />;
}
'use client';
 
import * as React from 'react';
 
import type { PlateElementProps } from 'platejs/react';
 
import { type VariantProps, cva } from 'class-variance-authority';
import { PlateElement } from 'platejs/react';
 
const headingVariants = cva(
  'relative mb-1 data-[nav-target=true]:rounded-md data-[nav-target=true]:bg-(--color-highlight)',
  {
    variants: {
      variant: {
        h1: 'mt-[1.6em] pb-1 font-bold font-heading text-4xl',
        h2: 'mt-[1.4em] pb-px font-heading font-semibold text-2xl tracking-tight',
        h3: 'mt-[1em] pb-px font-heading font-semibold text-xl tracking-tight',
        h4: 'mt-[0.75em] font-heading font-semibold text-lg tracking-tight',
        h5: 'mt-[0.75em] font-semibold text-lg tracking-tight',
        h6: 'mt-[0.75em] font-semibold text-base tracking-tight',
      },
    },
  }
);
 
export function HeadingElement({
  variant = 'h1',
  ...props
}: PlateElementProps & VariantProps<typeof headingVariants>) {
  return (
    <PlateElement
      as={variant!}
      className={headingVariants({ variant })}
      {...props}
    >
      {props.children}
    </PlateElement>
  );
}
 
export function H1Element(props: PlateElementProps) {
  return <HeadingElement variant="h1" {...props} />;
}
 
export function H2Element(props: PlateElementProps) {
  return <HeadingElement variant="h2" {...props} />;
}
 
export function H3Element(props: PlateElementProps) {
  return <HeadingElement variant="h3" {...props} />;
}
 
export function H4Element(props: PlateElementProps) {
  return <HeadingElement variant="h4" {...props} />;
}
 
export function H5Element(props: PlateElementProps) {
  return <HeadingElement variant="h5" {...props} />;
}
 
export function H6Element(props: PlateElementProps) {
  return <HeadingElement variant="h6" {...props} />;
}

Add Static Rendering

Use BaseBasicBlocksKit when rendering read-only content with platejs/static.

import {
  BaseBlockquotePlugin,
  BaseH1Plugin,
  BaseH2Plugin,
  BaseH3Plugin,
  BaseH4Plugin,
  BaseH5Plugin,
  BaseH6Plugin,
  BaseHorizontalRulePlugin,
} from '@platejs/basic-nodes';
import { BaseParagraphPlugin } from 'platejs';
 
import { BlockquoteElementStatic } from '@/components/ui/blockquote-node-static';
import {
  H1ElementStatic,
  H2ElementStatic,
  H3ElementStatic,
  H4ElementStatic,
  H5ElementStatic,
  H6ElementStatic,
} from '@/components/ui/heading-node-static';
import { HrElementStatic } from '@/components/ui/hr-node-static';
import { ParagraphElementStatic } from '@/components/ui/paragraph-node-static';
 
export const BaseBasicBlocksKit = [
  BaseParagraphPlugin.withComponent(ParagraphElementStatic),
  BaseH1Plugin.withComponent(H1ElementStatic),
  BaseH2Plugin.withComponent(H2ElementStatic),
  BaseH3Plugin.withComponent(H3ElementStatic),
  BaseH4Plugin.withComponent(H4ElementStatic),
  BaseH5Plugin.withComponent(H5ElementStatic),
  BaseH6Plugin.withComponent(H6ElementStatic),
  BaseBlockquotePlugin.withComponent(BlockquoteElementStatic),
  BaseHorizontalRulePlugin.withComponent(HrElementStatic),
];
import {
  BaseBlockquotePlugin,
  BaseH1Plugin,
  BaseH2Plugin,
  BaseH3Plugin,
  BaseH4Plugin,
  BaseH5Plugin,
  BaseH6Plugin,
  BaseHorizontalRulePlugin,
} from '@platejs/basic-nodes';
import { BaseParagraphPlugin } from 'platejs';
 
import { BlockquoteElementStatic } from '@/components/ui/blockquote-node-static';
import {
  H1ElementStatic,
  H2ElementStatic,
  H3ElementStatic,
  H4ElementStatic,
  H5ElementStatic,
  H6ElementStatic,
} from '@/components/ui/heading-node-static';
import { HrElementStatic } from '@/components/ui/hr-node-static';
import { ParagraphElementStatic } from '@/components/ui/paragraph-node-static';
 
export const BaseBasicBlocksKit = [
  BaseParagraphPlugin.withComponent(ParagraphElementStatic),
  BaseH1Plugin.withComponent(H1ElementStatic),
  BaseH2Plugin.withComponent(H2ElementStatic),
  BaseH3Plugin.withComponent(H3ElementStatic),
  BaseH4Plugin.withComponent(H4ElementStatic),
  BaseH5Plugin.withComponent(H5ElementStatic),
  BaseH6Plugin.withComponent(H6ElementStatic),
  BaseBlockquotePlugin.withComponent(BlockquoteElementStatic),
  BaseHorizontalRulePlugin.withComponent(HrElementStatic),
];

Ownership

LayerOwnerWhat It Does
@platejs/basic-nodesPackageExports base H1-H6 plugins, BaseHeadingPlugin, HeadingRules, and shared heading rules.
@platejs/basic-nodes/reactPackageExports H1Plugin through H6Plugin, plus the grouping HeadingPlugin.
basic-blocks-kitRegistryAdds H1-H6 React plugins with components, input rules, shortcuts, and reset-on-empty break behavior.
basic-blocks-base-kitRegistryAdds static H1-H6 components for server/static rendering.
heading-nodeRegistry UIRenders editable and static heading components.
Toolbar and slash UIRegistry UIProvides H1-H6 turn-into items, H1-H3 insert items, and H1-H3 slash-command items.
@platejs/markdownPackageSerializes and deserializes h1 through h6 as Markdown headings.

BasicBlocksPlugin and BaseBasicBlocksPlugin are package grouping plugins. They do not install registry UI components; use the registry kits when you want the Plate UI heading styles.

Manual Setup

Install Package

pnpm add @platejs/basic-nodes
pnpm add @platejs/basic-nodes

Add Individual Levels

Use individual level plugins when you want registry components, shortcuts, or per-level configuration.

import { HeadingRules } from '@platejs/basic-nodes';
import {
  H1Plugin,
  H2Plugin,
  H3Plugin,
  H4Plugin,
  H5Plugin,
  H6Plugin,
} from '@platejs/basic-nodes/react';
import { createPlateEditor } from 'platejs/react';
 
import {
  H1Element,
  H2Element,
  H3Element,
  H4Element,
  H5Element,
  H6Element,
} from '@/components/ui/heading-node';
 
export const editor = createPlateEditor({
  plugins: [
    H1Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H1Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+1' } },
    }),
    H2Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H2Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+2' } },
    }),
    H3Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H3Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+3' } },
    }),
    H4Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H4Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+4' } },
    }),
    H5Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H5Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+5' } },
    }),
    H6Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H6Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+6' } },
    }),
  ],
});
import { HeadingRules } from '@platejs/basic-nodes';
import {
  H1Plugin,
  H2Plugin,
  H3Plugin,
  H4Plugin,
  H5Plugin,
  H6Plugin,
} from '@platejs/basic-nodes/react';
import { createPlateEditor } from 'platejs/react';
 
import {
  H1Element,
  H2Element,
  H3Element,
  H4Element,
  H5Element,
  H6Element,
} from '@/components/ui/heading-node';
 
export const editor = createPlateEditor({
  plugins: [
    H1Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H1Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+1' } },
    }),
    H2Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H2Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+2' } },
    }),
    H3Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H3Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+3' } },
    }),
    H4Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H4Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+4' } },
    }),
    H5Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H5Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+5' } },
    }),
    H6Plugin.configure({
      inputRules: [HeadingRules.markdown()],
      node: { component: H6Element },
      rules: { break: { empty: 'reset' } },
      shortcuts: { toggle: { keys: 'mod+alt+6' } },
    }),
  ],
});

Use A Headless Group

Use BaseHeadingPlugin when you want to install several heading levels without configuring each level by hand.

import { BaseHeadingPlugin } from '@platejs/basic-nodes';
import { createSlateEditor } from 'platejs';
 
export const editor = createSlateEditor({
  plugins: [
    BaseHeadingPlugin.configure({
      options: {
        levels: [1, 2, 3],
      },
    }),
  ],
});
import { BaseHeadingPlugin } from '@platejs/basic-nodes';
import { createSlateEditor } from 'platejs';
 
export const editor = createSlateEditor({
  plugins: [
    BaseHeadingPlugin.configure({
      options: {
        levels: [1, 2, 3],
      },
    }),
  ],
});

Value Shape

Headings are normal block elements. The heading level is the node type.

const value = [
  {
    children: [{ text: 'Installation' }],
    type: 'h1',
  },
  {
    children: [{ text: 'Install packages' }],
    type: 'h2',
  },
  {
    children: [{ text: 'Configure editor plugins' }],
    type: 'h3',
  },
];
const value = [
  {
    children: [{ text: 'Installation' }],
    type: 'h1',
  },
  {
    children: [{ text: 'Install packages' }],
    type: 'h2',
  },
  {
    children: [{ text: 'Configure editor plugins' }],
    type: 'h3',
  },
];
TypeHTML ParserDefault Render Tag
h1H1<h1>
h2H2<h2>
h3H3<h3>
h4H4<h4>
h5H5<h5>
h6H6<h6>

BaseHeadingPlugin.configure({ options: { levels: 3 } }) creates H1 through H3. Passing an array such as [1, 3, 5] creates only those levels.

Editing Behavior

All heading levels share the same base behavior.

BehaviorSource
Toggle transformEach base plugin binds editor.tf.<level>.toggle() to editor.tf.toggleBlock(type).
Enter splitBase rules set break.splitReset: true; the registry kit also resets an empty heading on break.
Backspace at startBase rules set delete.start: 'reset'.
Empty mergeBase rules set merge.removeEmpty: true.
HTML pasteEach level deserializes its matching H1 through H6 tag.

Input Rules And Shortcuts

HeadingRules.markdown() creates a block-start input rule for the configured plugin key.

InputResult
# H1
## H2
### H3
#### H4
##### H5
###### H6

The registry kit also binds mod+alt+1 through mod+alt+6 to the matching toggle transform.

Registry UI

SurfaceHeading Levels
Turn Into toolbarH1-H6
Insert toolbarH1-H3
Slash commandH1-H3
Editable elementH1Element through H6Element
Static elementH1ElementStatic through H6ElementStatic

Static heading elements preserve an id field by rendering an internal anchor span. That anchor is used by DOCX table-of-contents links.

Markdown

@platejs/markdown maps Markdown heading depth to the matching Plate heading type.

# H1
## H2
### H3
# H1
## H2
### H3

Serialization uses the node type to choose Markdown depth. For example, type: 'h2' serializes as ##.

API Reference

APIPackageUse
BaseH1Plugin through BaseH6Plugin@platejs/basic-nodesHeadless heading level plugins.
BaseHeadingPlugin@platejs/basic-nodesGrouping plugin that creates nested heading levels from options.levels.
HeadingRules.markdown()@platejs/basic-nodesCreates the # through ###### block-start input rules.
H1Plugin through H6Plugin@platejs/basic-nodes/reactReact heading level plugins.
HeadingPlugin@platejs/basic-nodes/reactReact grouping plugin for heading levels.
editor.tf.h1.toggle() through editor.tf.h6.toggle()plugin-bound transformsToggle selected blocks to the matching heading level.