Static Rendering
A minimal, memoized, read-only version of Plate with RSC/SSR support.
<PlateStatic>
is a fast, read-only React component for rendering Slate content, optimized for server-side or React Server Component (RSC) environments. It avoids client-side editing logic and memoizes node renders for better performance compared to using <Plate>
in read-only mode.
It's a core part of serializeHtml
for HTML export and is ideal for any server or RSC context needing a non-interactive, presentational view of Plate content.
Key Advantages
- Server-Safe: No browser API dependencies; works in SSR/RSC.
- No Slate Editor Overhead: Excludes interactive features like selections or event handlers.
- Memoized Rendering: Uses
_memo
and structural checks to re-render only changed nodes. - Partial Re-Renders: Changes in one part of the document don't force a full re-render.
- Lightweight: Smaller bundle size as it omits interactive editor code.
When to Use <PlateStatic>
- Generating HTML with HTML Serialization.
- Displaying server-rendered previews in Next.js (especially with RSC).
- Building static sites with read-only Plate content.
- Optimizing performance-critical read-only views.
- Rendering AI-streaming content.
Interactive vs. Static
For interactive read-only features (like comment popovers or selections), use the standard <Plate>
component in the browser. For purely server-rendered, non-interactive content, <PlateStatic>
is the recommended choice.
Usage
Create a Slate Editor
Initialize a Slate editor instance using createSlateEditor
with your required plugins. This is analogous to using usePlateEditor
for the interactive <Plate>
component.
import { createSlateEditor } from '@udecode/plate';
// Import your desired base plugins (e.g., BaseHeadingPlugin, MarkdownPlugin)
// Ensure you are NOT importing from /react subpaths for server environments.
const editor = createSlateEditor({
plugins: [
// Add your list of base plugins here
// Example: BaseHeadingPlugin, MarkdownPlugin.configure({...})
],
value: [ // Example initial value
{
type: 'p',
children: [{ text: 'Hello from a static Plate editor!' }],
},
],
});
Define Static Node Components
If your interactive editor uses client-side components (e.g., with use client
or event handlers), you must create static, server-safe equivalents. These components should render pure HTML without browser-specific logic.
import React from 'react';
import type { SlateElementProps } from '@udecode/plate';
export function ParagraphElementStatic(props: SlateElementProps) {
return (
<SlateElement {...props}>
{props.children}
</SlateElement>
);
}
Create similar static components for headings, images, links, etc.
Map Plugin Keys to Static Components
Create an object that maps plugin keys or node types to their corresponding static React components.
import { ParagraphElementStatic } from './ui/paragraph-element-static';
import { HeadingElementStatic } from './ui/heading-element-static';
// ... import other static components
export const staticComponents = {
p: ParagraphElementStatic,
h1: HeadingElementStatic,
// ... add mappings for all your element and leaf types
};
Render <PlateStatic>
Use the <PlateStatic>
component, providing the editor
instance and your components
mapping.
import { PlateStatic } from '@udecode/plate';
import { createSlateEditor } from '@udecode/plate';
// import { BaseHeadingPlugin, ... } from '@udecode/plate-heading'; // etc.
import { staticComponents } from '@/components/static-components';
export default async function MyStaticPage() {
// Example: Fetch or define editor value
const initialValue = [
{ type: 'h1', children: [{ text: 'Server-Rendered Title' }] },
{ type: 'p', children: [{ text: 'Content rendered statically.' }] },
];
const editor = createSlateEditor({
plugins: [/* your base plugins */],
value: initialValue,
});
return (
<PlateStatic
editor={editor}
components={staticComponents}
style={{ padding: 16 }}
className="my-plate-static-content"
/>
);
}
Value Override
If you pass a value
prop directly to <PlateStatic>
, it will override editor.children
.
<PlateStatic
editor={editor}
components={staticComponents}
value={[
{ type: 'p', children: [{ text: 'Overridden content.' }] }
]}
/>
Memoization Details
<PlateStatic>
enhances performance through memoization:
- Each
<ElementStatic>
and<LeafStatic>
is wrapped inReact.memo
. - Reference Equality: Unchanged node references prevent re-renders.
_memo
Field: Settingnode._memo = true
(or any stable value) on an element or leaf can force Plate to skip re-rendering that specific node, even if its content changes. This is useful for fine-grained control over updates.
PlateStatic
vs. Plate
+ readOnly
Aspect | <PlateStatic> | <Plate> + readOnly |
---|---|---|
Interactivity | No (server-safe) | Some interactive features can run (browser-only) |
Browser APIs | Not used; safe for SSR/RSC | Minimal use, client-side context required |
Performance | Optimized for static rendering, minimal overhead | Heavier, includes more editor internals |
Partial Re-render | Memoized sub-trees for efficient updates | Supports partial re-renders, but with client overhead |
Use Cases | Server rendering, HTML serialization, static previews | Browser-based read-only states, interactive features |
Recommendation | SSR/RSC without editing or complex interactions | Client-side read-only states with interactive needs |
RSC/SSR Example
In a Next.js App Router (or similar RSC environment), <PlateStatic>
can be used directly in Server Components:
import { PlateStatic } from '@udecode/plate';
import { createSlateEditor } from '@udecode/plate';
// Example base plugins (ensure non-/react imports)
// import { BaseHeadingPlugin } from '@udecode/plate-heading';
import { staticComponents } from '@/components/static-components'; // Your static components mapping
export default async function Page() {
// Fetch or define content server-side
const serverContent = [
{ type: 'h1', children: [{ text: 'Rendered on the Server! 🎉' }] },
{ type: 'p', children: [{ text: 'This content is static and server-rendered.' }] },
];
const editor = createSlateEditor({
// plugins: [BaseHeadingPlugin, /* ...other base plugins */],
plugins: [], // Add your base plugins
value: serverContent,
});
return (
<PlateStatic
editor={editor}
components={staticComponents}
className="my-static-preview-container"
/>
);
}
This renders the content to HTML on the server without needing a client-side JavaScript bundle for PlateStatic
itself.
Pairing with serializeHtml
For generating a complete HTML string (e.g., for emails, PDFs, or external systems), use serializeHtml
. It utilizes <PlateStatic>
internally.
import { createSlateEditor, serializeHtml } from '@udecode/plate';
import { staticComponents } from '@/components/static-components';
// import { BaseHeadingPlugin, ... } from '@udecode/plate-heading';
async function getDocumentAsHtml(value: any[]) {
const editor = createSlateEditor({
plugins: [/* ...your base plugins... */],
value,
});
const html = await serializeHtml(editor, {
components: staticComponents,
// editorComponent: PlateStatic, // Optional: Defaults to PlateStatic
props: { className: 'prose max-w-none' }, // Example: Pass props to the root div
});
return html;
}
// Example Usage:
// const mySlateValue = [ { type: 'h1', children: [{ text: 'My Document' }] } ];
// getDocumentAsHtml(mySlateValue).then(console.log);
For more details, see the HTML Serialization guide.
API Reference
<PlateStatic>
Props
import type React from 'react';
import type { Descendant } from 'slate';
import type { PlateEditor, PlatePluginComponent } from '@udecode/plate/core'; // Adjust imports as per your setup
interface PlateStaticProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* The Slate editor instance, created via `createSlateEditor`.
* Must include plugins relevant to the content being rendered.
*/
editor: PlateEditor;
/**
* A record mapping plugin keys (or node types like 'p', 'h1')
* to their corresponding static React components.
*/
components: Record<string, PlatePluginComponent>;
/**
* Optional Slate `Value` (array of `Descendant` nodes).
* If provided, this will be used for rendering instead of `editor.children`.
*/
value?: Descendant[];
/** Inline CSS styles for the root `div` element. */
style?: React.CSSProperties;
// Other HTMLDivElement attributes like `className`, `id`, etc., are also supported.
}
editor
: An instance ofPlateEditor
created withcreateSlateEditor
.components
: Maps node types (e.g.,'p'
,'h1'
) or plugin keys to your static React components.value
: Optional. If provided, this array ofDescendant
nodes will be rendered, overriding the content currently ineditor.children
.
Next Steps
- Explore HTML Serialization for exporting content.
- Learn about using Plate in React Server Components.
- Refer to individual plugin documentation for their base (non-React) imports.