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.
Features
- Block
h1throughh6elements. - Individual
H1PluginthroughH6Pluginexports. - Grouping
HeadingPluginandBaseHeadingPluginwith configurable levels. - Bound
editor.tf.h1.toggle()througheditor.tf.h6.toggle()transforms. #through######input rules withHeadingRules.markdown().mod+alt+1throughmod+alt+6shortcuts 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
| Layer | Owner | What It Does |
|---|---|---|
@platejs/basic-nodes | Package | Exports base H1-H6 plugins, BaseHeadingPlugin, HeadingRules, and shared heading rules. |
@platejs/basic-nodes/react | Package | Exports H1Plugin through H6Plugin, plus the grouping HeadingPlugin. |
basic-blocks-kit | Registry | Adds H1-H6 React plugins with components, input rules, shortcuts, and reset-on-empty break behavior. |
basic-blocks-base-kit | Registry | Adds static H1-H6 components for server/static rendering. |
heading-node | Registry UI | Renders editable and static heading components. |
| Toolbar and slash UI | Registry UI | Provides H1-H6 turn-into items, H1-H3 insert items, and H1-H3 slash-command items. |
@platejs/markdown | Package | Serializes 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-nodespnpm add @platejs/basic-nodesAdd 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',
},
];| Type | HTML Parser | Default Render Tag |
|---|---|---|
h1 | H1 | <h1> |
h2 | H2 | <h2> |
h3 | H3 | <h3> |
h4 | H4 | <h4> |
h5 | H5 | <h5> |
h6 | H6 | <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.
| Behavior | Source |
|---|---|
| Toggle transform | Each base plugin binds editor.tf.<level>.toggle() to editor.tf.toggleBlock(type). |
| Enter split | Base rules set break.splitReset: true; the registry kit also resets an empty heading on break. |
| Backspace at start | Base rules set delete.start: 'reset'. |
| Empty merge | Base rules set merge.removeEmpty: true. |
| HTML paste | Each 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.
| Input | Result |
|---|---|
# | 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
| Surface | Heading Levels |
|---|---|
| Turn Into toolbar | H1-H6 |
| Insert toolbar | H1-H3 |
| Slash command | H1-H3 |
| Editable element | H1Element through H6Element |
| Static element | H1ElementStatic 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
### H3Serialization uses the node type to choose Markdown depth. For example, type: 'h2' serializes as ##.
API Reference
| API | Package | Use |
|---|---|---|
BaseH1Plugin through BaseH6Plugin | @platejs/basic-nodes | Headless heading level plugins. |
BaseHeadingPlugin | @platejs/basic-nodes | Grouping plugin that creates nested heading levels from options.levels. |
HeadingRules.markdown() | @platejs/basic-nodes | Creates the # through ###### block-start input rules. |
H1Plugin through H6Plugin | @platejs/basic-nodes/react | React heading level plugins. |
HeadingPlugin | @platejs/basic-nodes/react | React grouping plugin for heading levels. |
editor.tf.h1.toggle() through editor.tf.h6.toggle() | plugin-bound transforms | Toggle selected blocks to the matching heading level. |