From zbeyens. The source code is available on GitHub.

Plate
PlatePliteEditorsTemplates
GitHub16kGitHub
DiscordDiscord
  • Feature Kits
  • Plugin
    • Plugin Methods
    • Plugin Shortcuts
    • Plugin Context
    • Plugin Components
    • Plugin Rules
    • Editing Behavior
    • Plugin Input Rules
  • Editor
    • Editor Methods
    • Controlled Value
  • Performance
  • Static Rendering
  • HTML
  • Markdown
  • Form
  • TypeScript
  • Debugging
  • Unit Testing
  • Browser
  • Troubleshooting

Static Rendering

PreviousNext

A minimal, memoized, read-only version of Plate with RSC/SSR support.

<PlateStatic> is a fast, read-only React component for rendering Plate 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 is the rendering path behind renderStaticHtml and fits server or RSC surfaces that need a non-interactive Plate view.

Key Advantages

  • Server-Safe: No browser API dependencies; works in SSR/RSC.
  • No Plate Editor Overhead: Excludes interactive features like selections or event handlers.
  • Memoized Rendering: Uses structural identity 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

PerformanceHTML

On This Page

Key AdvantagesWhen to Use <PlateStatic>Kit UsageInstallationAdd KitExampleManual UsageCreate a Plite EditorDefine Static Node ComponentsMap Schema Identities to Static ComponentsRender <PlateStatic>Memoization DetailsClient-Side Alternative: PlateViewExample: Server Component with Both Static ViewsExample: Client Component with PlateViewKey Features of PlateViewPlateStatic vs. PlateView vs. Plate + readOnlyRSC/SSR ExamplePairing with renderStaticHtmlAPI Reference<PlateStatic> PropsNext Steps
Build your editor
Production-ready AI template and reusable components.
Get all-access
<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.

Kit Usage

Installation

The fastest way to enable static rendering is with the BaseEditorKit, which includes pre-configured base plugins that work seamlessly with server-side rendering.

import { BaseAlignKit } from './align-static';
import { BaseBasicBlocksKit } from './basic-blocks-static';
import { BaseBasicMarksKit } from './basic-marks-static';
import { BaseCalloutKit } from './callout-static';
import { BaseCodeBlockKit } from './code-block-static';
import { BaseColumnKit } from './column-static';
import { BaseCommentKit } from './comment-static';
import { BaseDateKit } from './date-static';
import { BaseFontKit } from './font-static';
import { BaseFootnoteKit } from './footnote-static';
import { BaseLineHeightKit } from './line-height-static';
import { BaseLinkKit } from './link-static';
import { BaseListKit } from './list-static';
import { MarkdownKit } from './markdown';
import { BaseMathKit } from './math-static';
import { BaseMediaKit } from './media-static';
import { BaseMentionKit } from './mention-static';
import { BaseSuggestionKit } from './suggestion-static';
import { BaseTableKit } from './table-static';
import { BaseTocKit } from './toc-static';
import { BaseToggleKit } from './toggle-static';
 
export const BaseEditorKit = [
  ...BaseBasicBlocksKit,
  ...BaseCodeBlockKit,
  ...BaseTableKit,
  ...BaseToggleKit,
  ...BaseTocKit,
  ...BaseMediaKit,
  ...BaseCalloutKit,
  ...BaseColumnKit,
  ...BaseMathKit,
  ...BaseDateKit,
  ...BaseLinkKit,
  ...BaseMentionKit,
  ...BaseFootnoteKit,
  ...BaseBasicMarksKit,
  ...BaseFontKit,
  ...BaseListKit,
  ...BaseAlignKit,
  ...BaseLineHeightKit,
  ...BaseCommentKit,
  ...BaseSuggestionKit,
  ...MarkdownKit,
] as const;
import { BaseAlignKit } from './align-static';
import { BaseBasicBlocksKit } from './basic-blocks-static';
import { BaseBasicMarksKit } from './basic-marks-static';
import { BaseCalloutKit } from './callout-static';
import { BaseCodeBlockKit } from './code-block-static';
import { BaseColumnKit } from './column-static';
import { BaseCommentKit } from './comment-static';
import { BaseDateKit } from './date-static';
import { BaseFontKit } from './font-static';
import { BaseFootnoteKit } from './footnote-static';
import { BaseLineHeightKit } from './line-height-static';

































Add Kit

import { createBaseEditor } from 'platejs';
import { PlateStatic } from 'platejs/static';
import { BaseEditorKit } from '@/components/editor/plugins-static';
 
const editor = createBaseEditor({
  plugins: BaseEditorKit,
  initialValue: [
    { type: 'heading', level: 1, children: [{ text: 'Server-Rendered Title' }] },
    { type: 'paragraph', children: [{ text: 'This content is rendered statically.' }] },
  ],
});
 
// Render statically
export default function MyStaticPage() {
  return <PlateStatic editor={editor} />;
}

Example

See a complete server-side static rendering example:

import fs from 'node:fs/promises';
import path from 'node:path';
 
import { cva } from 'class-variance-authority';
import type { Metadata } from 'next';
import type { EditorDocumentValue } from 'platejs';
import { createStaticEditor, renderStaticHtml } from 'platejs/static';
import * as React from 'react';
 
import { EditorStatic } from '@/components/editor/editor-static';
import {
  EditorClient,
  EditorViewClient,
  ExportHtmlButton,
  HtmlIframe,
















































































































































Manual Usage

Create a Plite Editor

Initialize a Plite editor instance using createBaseEditor with your required plugins and components. This is analogous to using usePlateEditor for the interactive <Plate> component.

lib/plate-static-editor.ts
import { createBaseEditor } from 'platejs';
import { BaseBasicBlocksKit } from '@/components/editor/basic-blocks-static';
// Import any other desired base plugins, such as MarkdownPlugin.
// Ensure you are NOT importing from /react subpaths for server environments.
 
const editor = createBaseEditor({
  plugins: [
    ...BaseBasicBlocksKit,
    // Add other base plugins here.
  ],
  initialValue: [
    {
      type: 'paragraph',
      children: [{ text: 'Hello from a static Plate editor!' }],
    },
  ],
});
lib/plate-static-editor.ts
import { createBaseEditor } from 'platejs';
import { BaseBasicBlocksKit } from '@/components/editor/basic-blocks-static';
// Import any other desired base plugins, such as MarkdownPlugin.
// Ensure you are NOT importing from /react subpaths for server environments.
 
const editor = createBaseEditor({
  plugins: [
    ...BaseBasicBlocksKit,
    // Add other base plugins here.
  ],
  initialValue: [
    {
      type: 'paragraph',
      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.

components/editor/paragraph-static.tsx
import React from 'react';
import type { BaseParagraphPlugin } from 'platejs';
import type { PliteElementProps } from 'platejs/static';
 
export function ParagraphElementStatic(
  props: PliteElementProps<typeof BaseParagraphPlugin>
) {
  return (
    <PliteElement {...props}>
      {props.children}
    </PliteElement>
  );
}

Create similar static components for headings, images, links, etc.

Map Schema Identities to Static Components

Create an object that maps persisted element types or property keys to their corresponding static React components, then pass it to the editor.

components/static-components.ts
import { ParagraphElementStatic } from './ui/paragraph-static';
import { HeadingElementStatic } from './ui/heading-static';
// ... import other static components
 
export const staticComponents = {
  p: ParagraphElementStatic,
  h1: HeadingElementStatic,
  // ... add mappings for all your element and leaf types
};
components/static-components.ts
import { ParagraphElementStatic } from './ui/paragraph-static';
import { HeadingElementStatic } 






Render <PlateStatic>

Use the <PlateStatic> component, providing the editor instance configured with your components.

app/my-static-page/page.tsx (RSC Example)
import { createBaseEditor } from 'platejs';
import { PlateStatic } from 'platejs/static';
import { BaseBasicBlocksKit } from '@/components/editor/basic-blocks-static';
import { staticComponents } from '@/components/static-components';
 
export default async function MyStaticPage() {
  // Example: Fetch or define editor value
  const initialValue = [
    { type: 'heading', level: 1, children: [{ text: 'Server-Rendered Title' }] },
    { type: 'paragraph', children: [{ text: 'Content rendered statically.' }] },
  ];
 
  const editor = createBaseEditor












Memoization Details

<PlateStatic> enhances performance through memoization:

  • Each <ElementStatic> and <LeafStatic> is wrapped in React.memo.
  • Reference Equality: Unchanged node references prevent re-renders.

Client-Side Alternative: PlateView

For cases where you need minimal interactivity with static content, use <PlateView>. This component wraps <PlateStatic> and adds client-side event handlers for user interactions while maintaining the performance benefits of static rendering.

Example: Server Component with Both Static Views

app/document/page.tsx
import { createStaticEditor, PlateStatic } from 'platejs/static';
import { BaseEditorKit } from '@/components/editor/plugins-static';
import { InteractiveViewer } from './interactive-viewer';
 
export default async function DocumentPage() {
  const content = await fetchDocument(); // Your document data
  // Server-side static editor
  const editor = createStaticEditor({
    plugins: BaseEditorKit,
    initialValue: content,
  });
 
  return (
    <div className="grid grid-cols-2 gap-4">
      {/* Pure static rendering - no interactivity */}
      <div>
        <h2>Static View (Server Rendered)</h2>
        <PlateStatic editor={editor} />
      </div>
 
      {/* Interactive view - rendered on client */}
      <div>
        <h2>Interactive View</h2>
        <InteractiveViewer value={content} />
      </div>
    </div>
  );
}
app/document/page.tsx
import { createStaticEditor, PlateStatic } from 'platejs/static';
import { BaseEditorKit } from '@/components/editor/plugins-static';
import { InteractiveViewer } from './interactive-viewer';
 
export default async function DocumentPage() {
  const content = await fetchDocument(); // Your document data
  // Server-side static editor
  const editor = createStaticEditor({
    plugins: BaseEditorKit,
    initialValue: content,
  });
 
  return (
    <div className="grid grid-cols-2 gap-4">
      {/* Pure static rendering - no interactivity */}
      <div











Example: Client Component with PlateView

app/document/interactive-viewer.tsx
'use client';
 
import { usePlateViewEditor } from 'platejs/react';
import { PlateView } from 'platejs/react';
import { BaseEditorKit } from '@/components/editor/plugins-static';
 
export function InteractiveViewer({ value }) {
  const editor = usePlateViewEditor({
    plugins: BaseEditorKit,
    initialValue: value,
  });
 
  return <PlateView editor={editor} />;
}
app/document/interactive-viewer.tsx
'use client';
 
import { usePlateViewEditor } from 'platejs/react';
import { PlateView } from 'platejs/react';
import { BaseEditorKit } from '@/components/editor/plugins-static';
 
export function InteractiveViewer({ value }) {
  const editor = usePlateViewEditor({
    plugins: BaseEditorKit,
    initialValue: value,
  });
 
  return <PlateView editor={editor} />;
}

Key Features of PlateView

  • Client-side only: Requires 'use client' directive
  • Adds interactivity: Enables user interactions with the content (e.g., text selection, copying, future interactions like tooltips, highlights, etc.)
  • Minimal overhead: Still uses PlateStatic internally for rendering
  • Use with usePlateViewEditor: Creates a static editor optimized for view-only React components
  • ViewPlugin included: The static editor automatically includes ViewPlugin which provides event handling capabilities
Server Component Compatibility

PlateView cannot be used in Server Components. If you're passing an editor from a server component to a client component, you'll encounter serialization errors. Use PlateStatic on the server side, or create the editor client-side with usePlateViewEditor.

PlateStatic vs. PlateView vs. Plate + readOnly

Aspect<PlateStatic><PlateView><Plate> + readOnly
EnvironmentServer/Client (SSR/RSC safe)Client-onlyClient-only
InteractivityNoneMinimal (selection, copy, toolbar, etc.)Full interactive features (browser-only)
Browser APIsNot usedMinimal (event handlers)Full usage
PerformanceBest - static HTML onlyGood - static rendering + event delegationHeavier - full editor internals
Bundle SizeSmallestSmallLargest
Use CasesServer rendering, HTML exportClient-side content with basic interactionsFull read-only editor with all features
RecommendationSSR/RSC without any interactionsClient-side content needing light interactivityClient-side with complex interactive needs

RSC/SSR Example

In a Next.js App Router (or similar RSC environment), <PlateStatic> can be used directly in Server Components:

app/preview/page.tsx (RSC)
import { createBaseEditor } from 'platejs';
import { PlateStatic } from 'platejs/static';
import { BaseBasicBlocksKit } from '@/components/editor/basic-blocks-static';
import { staticComponents } from '@/components/static-components'; // Your static components mapping
 
export default async function Page() {
  // Fetch or define content server-side
  const serverContent = [
    { type: 'heading', level: 1, children: [{ text: 'Rendered on the Server! 🎉' }] },
    { type: 'paragraph', children: [{ text: 'This content is static and server-rendered.' }] },
  ];
 
  const editor = createBaseEditor({
    plugins: [...BaseBasicBlocksKit],
    components: staticComponents,
    initialValue: serverContent,
  });
 
  return (
    <PlateStatic
      editor={editor}
      className="my-static-preview-container"
    />
  );
}
app/preview/page.tsx (RSC)
import { createBaseEditor } from 'platejs';
import { PlateStatic } from 'platejs/static';
import { BaseBasicBlocksKit } from '@/components/editor/basic-blocks-static';
import { staticComponents } from '@/components/static-components'; // Your static components mapping
 
export default async function Page() {
  // Fetch or define content server-side
  const serverContent = [
    { type: 'heading', level: 1, children: [{ text: 'Rendered on the Server! 🎉' }] },
    { type: 'paragraph', children: [{ text: 'This content is static and server-rendered.' }] },
  ];
 
  const editor = createBaseEditor({
    plugins: [










This renders the content to HTML on the server without needing a client-side JavaScript bundle for PlateStatic itself.

Pairing with renderStaticHtml

For server-rendering the static Plate tree to an HTML string, use renderStaticHtml. It renders <PlateStatic> through React DOM Server; it is not a semantic HTML codec.

lib/html-serializer.ts
import { createBaseEditor } from 'platejs';
import { renderStaticHtml } from 'platejs/static';
import { BaseBasicBlocksKit } from '@/components/editor/basic-blocks-static';
import { staticComponents } from '@/components/static-components';
 
async function getDocumentAsHtml(value: any[]) {
  const editor = createBaseEditor({
    plugins: [...BaseBasicBlocksKit],
    components: staticComponents,
    initialValue: value,
  });
 
  const html = await renderStaticHtml(editor, {
    // editorComponent: PlateStatic, // Optional: Defaults to PlateStatic
    props: { className: 'prose max-w-none' }, // Example: Pass props to the root div
  });
 
  return html;
}
 
// Example Usage:
// const value = [ { type: 'heading', level: 1, children: [{ text: 'My Document' }] } ];
// getDocumentAsHtml(value).then(console.log);
lib/html-serializer.ts
import { createBaseEditor } from 'platejs';
import { renderStaticHtml } from 'platejs/static';
import { BaseBasicBlocksKit } from '@/components/editor/basic-blocks-static';
import { staticComponents } from '@/components/static-components';
 
async function getDocumentAsHtml(value: any[]) {
  const editor = createBaseEditor({
    plugins: [...BaseBasicBlocksKit],
    components: staticComponents,
    initialValue: value,
  });
 
  const html = await renderStaticHtml(editor, {
    // editorComponent: PlateStatic, // Optional: Defaults to PlateStatic
    props: { className: 'prose max-w-none' }, // Example: Pass props to the root div







For more details, see the HTML Serialization guide.

API Reference

<PlateStatic> Props

import type React from 'react';
import type { BaseEditor } from 'platejs';
 
interface PlateStaticProps<E = BaseEditor>
  extends React.HTMLAttributes<HTMLDivElement> {
  /**
   * The Plate editor instance, created via `createBaseEditor`.
   * Must include plugins and components relevant to the content being rendered.
   */
  editor: E;
 
  /** Inline CSS styles for the root `div` element. */
  style?: React.CSSProperties;
 
  // Other HTMLDivElement attributes like `className`, `id`, etc., are also supported.
}
import type React from 'react';
import type { BaseEditor } from 'platejs';
 
interface PlateStaticProps<E = BaseEditor>
  extends React.HTMLAttributes<HTMLDivElement> {
  /**
   * The Plate editor instance, created via `createBaseEditor`.
   * Must include plugins and components relevant to the content being rendered.
   */
  editor: E;
 
  /** Inline CSS styles for the root `div` element. */
  style?: React.CSSProperties;
 
  // Other HTMLDivElement attributes like `className`, `id`, etc., are also supported.
}
  • editor: An editor created with createBaseEditor or createStaticEditor, including the plugins and components required by the value.

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.
import { BaseLinkKit } from './link-static';
import { BaseListKit } from './list-static';
import { MarkdownKit } from './markdown';
import { BaseMathKit } from './math-static';
import { BaseMediaKit } from './media-static';
import { BaseMentionKit } from './mention-static';
import { BaseSuggestionKit } from './suggestion-static';
import { BaseTableKit } from './table-static';
import { BaseTocKit } from './toc-static';
import { BaseToggleKit } from './toggle-static';
export const BaseEditorKit = [
...BaseBasicBlocksKit,
...BaseCodeBlockKit,
...BaseTableKit,
...BaseToggleKit,
...BaseTocKit,
...BaseMediaKit,
...BaseCalloutKit,
...BaseColumnKit,
...BaseMathKit,
...BaseDateKit,
...BaseLinkKit,
...BaseMentionKit,
...BaseFootnoteKit,
...BaseBasicMarksKit,
...BaseFontKit,
...BaseListKit,
...BaseAlignKit,
...BaseLineHeightKit,
...BaseCommentKit,
...BaseSuggestionKit,
...MarkdownKit,
] as const;
import { createBaseEditor } from 'platejs';
import { PlateStatic } from 'platejs/static';
import { BaseEditorKit } from '@/components/editor/plugins-static';
 
const editor = createBaseEditor({
  plugins: BaseEditorKit,
  initialValue: [
    { type: 'heading', level: 1, children: [{ text: 'Server-Rendered Title' }] },
    { type: 'paragraph', children: [{ text: 'This content is rendered statically.' }] },
  ],
});
 
// Render statically
export default function MyStaticPage() {
  return <PlateStatic editor={editor} />;
}
}
from
'@/components/editor/plate-to-html'
;
import { PlateToHtmlEditorKit } from '@/components/editor/plate-to-html-kit';
import { alignValue } from '@/registry/examples/values/align-value';
import { basicBlocksValue } from '@/registry/examples/values/basic-blocks-value';
import { basicMarksValue } from '@/registry/examples/values/basic-marks-value';
import { columnValue } from '@/registry/examples/values/column-value';
import { dateValue } from '@/registry/examples/values/date-value';
import { discussionValue } from '@/registry/examples/values/discussion-value';
import { equationValue } from '@/registry/examples/values/equation-value';
import { fontValue } from '@/registry/examples/values/font-value';
import { indentValue } from '@/registry/examples/values/indent-value';
import { lineHeightValue } from '@/registry/examples/values/line-height-value';
import { linkValue } from '@/registry/examples/values/link-value';
import { listValue } from '@/registry/examples/values/list-value';
import { mediaValue } from '@/registry/examples/values/media-value';
import { mentionValue } from '@/registry/examples/values/mention-value';
import { tableValue } from '@/registry/examples/values/table-value';
import { tocPlaygroundValue } from '@/registry/examples/values/toc-value';
export const metadata: Metadata = {
title: 'Plate to HTML',
};
const getCachedTailwindCss = React.cache(async () => {
const cssPath = path.join(process.cwd(), 'public', 'tailwind.css');
return await fs.readFile(cssPath, 'utf-8');
});
const createHtmlDocument = ({
editorHtml,
katexCDN,
tailwindCss,
theme,
}: {
editorHtml: string;
tailwindCss: string;
katexCDN?: string;
theme?: string;
}) => `<!DOCTYPE html>
<html lang="en"${theme === 'dark' ? ' class="dark"' : ''}>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<style>${tailwindCss}</style>
${katexCDN}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&family=JetBrains+Mono:wght@400..700&display=swap"
rel="stylesheet"
/>
<style>
:root {
--font-sans: 'Inter', 'Inter Fallback';
--font-mono: 'JetBrains Mono', 'JetBrains Mono Fallback';
}
</style>
</head>
<body>
${editorHtml}
</body>
</html>`;
const createValue = (): EditorDocumentValue => ({
children: [
...basicBlocksValue,
...basicMarksValue,
...tocPlaygroundValue,
...linkValue,
...tableValue,
...equationValue,
...columnValue,
...mentionValue,
...dateValue,
...fontValue,
...discussionValue,
...alignValue,
...lineHeightValue,
...indentValue,
...listValue,
...mediaValue.children,
],
});
export default async function PlateToHtmlBlock() {
const editor = createStaticEditor({
plugins: PlateToHtmlEditorKit,
initialValue: createValue(),
});
const tailwindCss = await getCachedTailwindCss();
const katexCDN = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/katex.css" integrity="sha384-9PvLvaiSKCPkFKB1ZsEoTjgnJn+O3KvEwtsz37/XrkYft3DTk2gHdYvd9oWgW3tV" crossorigin="anonymous">`;
// const cookieStore = await cookies();
// const theme = cookieStore.get('theme')?.value;
const theme = 'light';
// Get the editor content HTML using EditorStatic
const editorHtml = await renderStaticHtml(editor, {
editorComponent: EditorStatic,
props: { style: { padding: '0 calc(50% - 350px)', paddingBottom: '' } },
});
// Create the full HTML document
const html = createHtmlDocument({
editorHtml,
katexCDN,
tailwindCss,
theme,
});
return (
<div className="grid grid-cols-3 px-4">
<div className="p-2">
<h3 className={headingVariants()}>Editor</h3>
<EditorClient value={createValue()} />
</div>
<div className="p-2">
<h3 className={headingVariants()}>EditorView</h3>
<EditorViewClient value={createValue()} />
</div>
<div className="relative p-2">
<h3 className={headingVariants()}>HTML Iframe</h3>
<ExportHtmlButton
className="absolute top-10 right-0"
html={html}
serverTheme={theme}
/>
<HtmlIframe
className="h-[7500px] w-full"
html={html}
serverTheme={theme}
/>
</div>
</div>
);
}
const headingVariants = cva(
'group mt-8 scroll-m-20 font-heading font-semibold text-xl tracking-tight'
);
import fs from 'node:fs/promises';
import path from 'node:path';
 
import { cva } from 'class-variance-authority';
import type { Metadata } from 'next';
import type { EditorDocumentValue } from 'platejs';
import { createStaticEditor, renderStaticHtml } from 'platejs/static';
import * as React from 'react';
 
import { EditorStatic } from '@/components/editor/editor-static';
import {
  EditorClient,
  EditorViewClient,
  ExportHtmlButton,
  HtmlIframe,
} from '@/components/editor/plate-to-html';
import { PlateToHtmlEditorKit } from '@/components/editor/plate-to-html-kit';
import { alignValue } from '@/registry/examples/values/align-value';
import { basicBlocksValue } from '@/registry/examples/values/basic-blocks-value';
import { basicMarksValue } from '@/registry/examples/values/basic-marks-value';
import { columnValue } from '@/registry/examples/values/column-value';
import { dateValue } from '@/registry/examples/values/date-value';
import { discussionValue } from '@/registry/examples/values/discussion-value';
import { equationValue } from '@/registry/examples/values/equation-value';
import { fontValue } from '@/registry/examples/values/font-value';
import { indentValue } from '@/registry/examples/values/indent-value';
import { lineHeightValue } from '@/registry/examples/values/line-height-value';
import { linkValue } from '@/registry/examples/values/link-value';
import { listValue } from '@/registry/examples/values/list-value';
import { mediaValue } from '@/registry/examples/values/media-value';
import { mentionValue } from '@/registry/examples/values/mention-value';
import { tableValue } from '@/registry/examples/values/table-value';
import { tocPlaygroundValue } from '@/registry/examples/values/toc-value';
 
export const metadata: Metadata = {
  title: 'Plate to HTML',
};
 
const getCachedTailwindCss = React.cache(async () => {
  const cssPath = path.join(process.cwd(), 'public', 'tailwind.css');
 
  return await fs.readFile(cssPath, 'utf-8');
});
 
const createHtmlDocument = ({
  editorHtml,
  katexCDN,
  tailwindCss,
  theme,
}: {
  editorHtml: string;
  tailwindCss: string;
  katexCDN?: string;
  theme?: string;
}) => `<!DOCTYPE html>
<html lang="en"${theme === 'dark' ? ' class="dark"' : ''}>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="color-scheme" content="light dark" />
    <style>${tailwindCss}</style>
    ${katexCDN}
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&family=JetBrains+Mono:wght@400..700&display=swap"
      rel="stylesheet"
    />
    <style>
      :root {
        --font-sans: 'Inter', 'Inter Fallback';
        --font-mono: 'JetBrains Mono', 'JetBrains Mono Fallback';
      }
    </style>
  </head>
  <body>
    ${editorHtml}
  </body>
</html>`;
 
const createValue = (): EditorDocumentValue => ({
  children: [
    ...basicBlocksValue,
    ...basicMarksValue,
    ...tocPlaygroundValue,
    ...linkValue,
    ...tableValue,
    ...equationValue,
    ...columnValue,
    ...mentionValue,
    ...dateValue,
    ...fontValue,
    ...discussionValue,
    ...alignValue,
    ...lineHeightValue,
    ...indentValue,
    ...listValue,
    ...mediaValue.children,
  ],
});
 
export default async function PlateToHtmlBlock() {
  const editor = createStaticEditor({
    plugins: PlateToHtmlEditorKit,
    initialValue: createValue(),
  });
 
  const tailwindCss = await getCachedTailwindCss();
  const katexCDN = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/katex.css" integrity="sha384-9PvLvaiSKCPkFKB1ZsEoTjgnJn+O3KvEwtsz37/XrkYft3DTk2gHdYvd9oWgW3tV" crossorigin="anonymous">`;
 
  // const cookieStore = await cookies();
  // const theme = cookieStore.get('theme')?.value;
  const theme = 'light';
 
  // Get the editor content HTML using EditorStatic
  const editorHtml = await renderStaticHtml(editor, {
    editorComponent: EditorStatic,
    props: { style: { padding: '0 calc(50% - 350px)', paddingBottom: '' } },
  });
 
  // Create the full HTML document
  const html = createHtmlDocument({
    editorHtml,
    katexCDN,
    tailwindCss,
    theme,
  });
 
  return (
    <div className="grid grid-cols-3 px-4">
      <div className="p-2">
        <h3 className={headingVariants()}>Editor</h3>
        <EditorClient value={createValue()} />
      </div>
 
      <div className="p-2">
        <h3 className={headingVariants()}>EditorView</h3>
        <EditorViewClient value={createValue()} />
      </div>
 
      <div className="relative p-2">
        <h3 className={headingVariants()}>HTML Iframe</h3>
        <ExportHtmlButton
          className="absolute top-10 right-0"
          html={html}
          serverTheme={theme}
        />
        <HtmlIframe
          className="h-[7500px] w-full"
          html={html}
          serverTheme={theme}
        />
      </div>
    </div>
  );
}
 
const headingVariants = cva(
  'group mt-8 scroll-m-20 font-heading font-semibold text-xl tracking-tight'
);
components/editor/paragraph-static.tsx
import React from 'react';
import type { BaseParagraphPlugin } from 'platejs';
import type { PliteElementProps } from 'platejs/static';
 
export function ParagraphElementStatic(
  props: PliteElementProps<typeof BaseParagraphPlugin>
) {
  return (
    <PliteElement {...props}>
      {props.children}
    </PliteElement>
  );
}
from
'./ui/heading-static'
;
// ... import other static components
export const staticComponents = {
p: ParagraphElementStatic,
h1: HeadingElementStatic,
// ... add mappings for all your element and leaf types
};
({
plugins: [...BaseBasicBlocksKit],
components: staticComponents,
initialValue,
});
return (
<PlateStatic
editor={editor}
style={{ padding: 16 }}
className="my-plate-static-content"
/>
);
}
app/my-static-page/page.tsx (RSC Example)
import { createBaseEditor } from 'platejs';
import { PlateStatic } from 'platejs/static';
import { BaseBasicBlocksKit } from '@/components/editor/basic-blocks-static';
import { staticComponents } from '@/components/static-components';
 
export default async function MyStaticPage() {
  // Example: Fetch or define editor value
  const initialValue = [
    { type: 'heading', level: 1, children: [{ text: 'Server-Rendered Title' }] },
    { type: 'paragraph', children: [{ text: 'Content rendered statically.' }] },
  ];
 
  const editor = createBaseEditor({
    plugins: [...BaseBasicBlocksKit],
    components: staticComponents,
    initialValue,
  });
 
  return (
    <PlateStatic
      editor={editor}
      style={{ padding: 16 }}
      className="my-plate-static-content"
    />
  );
}
>
<h2>Static View (Server Rendered)</h2>
<PlateStatic editor={editor} />
</div>
{/* Interactive view - rendered on client */}
<div>
<h2>Interactive View</h2>
<InteractiveViewer value={content} />
</div>
</div>
);
}
...
BaseBasicBlocksKit],
components: staticComponents,
initialValue: serverContent,
});
return (
<PlateStatic
editor={editor}
className="my-static-preview-container"
/>
);
}
});
return html;
}
// Example Usage:
// const value = [ { type: 'heading', level: 1, children: [{ text: 'My Document' }] } ];
// getDocumentAsHtml(value).then(console.log);