By default, Plate plugins are headless, meaning all nodes will be rendered as plain text. This guide will show you how to create and style custom components for your editor.
Plate UI
Unless you prefer to build everything from scratch, we recommend using Plate UI to get started. Plate UI is a collection of components that you can copy into your app and modify to suit your needs.
The process of creating and registering components is similar whether you use Plate UI or build your own from scratch.
Defining Components
Use PlateElement
for element nodes (like paragraphs, headings) and PlateLeaf
for text leaf nodes (like bold, italic). These components handle applying the necessary Plate props to your custom HTML elements.
Ensure the children
prop is rendered unconditionally for the editor to function correctly, even for void nodes.
Element Component
import { type PlateElementProps, PlateElement } from 'platejs/react';
export function BlockquoteElement(props: PlateElementProps) {
// props contains attributes, children, element, editor, etc.
// plus any custom props your plugin might pass.
return (
<PlateElement
as="blockquote"
className="my-1 border-l-2 pl-6 italic" // Apply custom styles directly
{...props} // Pass all original props (attributes, children, element, etc.)
/>
);
}
This example defines a BlockquoteElement
. The as
prop on PlateElement
specifies that it should render an HTML <blockquote>
. PlateElement
handles rendering the children
passed via {...props}
.
Leaf Component
import { type PlateLeafProps, PlateLeaf } from 'platejs/react';
export function CodeLeaf(props: PlateLeafProps) {
// props contains attributes, children, leaf, text, editor, etc.
// plus any custom props your plugin might pass.
return (
<PlateLeaf
as="code"
className="rounded-md bg-muted px-[0.3em] py-[0.2em] font-mono text-sm" // Apply custom styles
{...props} // Pass all original props (attributes, children, leaf, text, etc.)
/>
);
}
Styling
We recommend styling components using Tailwind CSS, as demonstrated in Plate UI.
Alternatively, Plate generates class names like slate-<node-type>
(e.g., slate-p
for paragraphs, slate-h1
for H1 headings) which you can target with global CSS:
.slate-p {
margin-bottom: 1rem;
}
.slate-bold {
font-weight: bold;
}
Register Components
To use your custom components, register them with the corresponding plugin or directly in the editor configuration.
Method 1: Plugin's withComponent
(Recommended)
The withComponent
method is the most straightforward way to associate a component with a plugin.
const plugins = [
// This is equivalent to:
// ParagraphPlugin.configure({ node: { component: MyParagraphElement }});
ParagraphPlugin.withComponent(MyParagraphElement),
CodeBlockPlugin.withComponent(MyCodeBlockElement),
CodeLinePlugin.withComponent(MyCodeLineElement),
CodeSyntaxPlugin.withComponent(MyCodeSyntaxLeaf),
]
Method 2: Plugin override.components
For plugins that manage multiple component parts (like CodeBlockPlugin
with code_block
, code_line
, and code_syntax
), or when you need to override components for a specific plugin instance, use the override.components
option within configure
.
const plugins = [
CodeBlockPlugin.configure({
override: {
components: {
[CodeBlockPlugin.key]: MyCodeBlockElement,
[CodeLinePlugin.key]: MyCodeLineElement,
[CodeSyntaxPlugin.key]: MyCodeSyntaxLeaf,
},
},
}),
];
Method 3: Editor components
Option
You can globally map plugin keys to components in createPlateEditor
(or usePlateEditor
). This is useful for managing all components in one place, or for plugins composed of multiple plugins.
const editor = createPlateEditor({
plugins: [ParagraphPlugin, CodeBlockPlugin /* ...other plugins */],
components: {
[ParagraphPlugin.key]: MyParagraphElement,
[CodeBlockPlugin.key]: MyCodeBlockElement,
[CodeLinePlugin.key]: MyCodeLineElement,
[CodeSyntaxPlugin.key]: MyCodeSyntaxLeaf,
// ...other component overrides
},
});