Skip to content

Output Modes ​

Editu can export editor content in three formats: Markdown, JSON, and HTML. Each format serves a different purpose — choose the one that fits your use case.

Quick Comparison ​

FormatMethodBest For
Markdowneditor.getMarkdown()Storage, Git, static sites, CMS
JSONeditor.getJSON()Database storage, API transfer, re-import
HTMLeditor.getHTML()Email, rendering outside editor, SSR

Markdown Output ​

The default and most portable format. Standard blocks (headings, lists, code, tables) serialize as standard Markdown. Layout blocks serialize as raw HTML with data-editu-* attributes since they have no Markdown equivalent.

typescript
const markdown = editor.getMarkdown();
// # Hello World
// 
// <div data-editu-group data-padding="md" data-background="muted">
// Content inside the group...
// </div>

TIP

Use the flavor option to control Markdown dialect: 'gfm' (default), 'commonmark', 'obsidian', or 'docusaurus'. See Getting Started - Markdown Flavor.

JSON Output ​

The lossless format — every node, attribute, and mark is preserved exactly. Ideal for storing in a database and re-importing later with editor.commands.setContent(json).

typescript
const json = editor.getJSON();
// {
//   type: 'doc',
//   content: [
//     { type: 'heading', attrs: { level: 1 }, content: [...] },
//     { type: 'group', attrs: { padding: 'md', background: 'muted', ... }, content: [...] },
//   ]
// }

INFO

JSON is the only format that fully round-trips layout block attributes. Markdown and HTML both preserve the data, but JSON maps 1:1 to the internal ProseMirror document structure.

HTML Output ​

Generates a complete HTML string of the document. Layout blocks render as styled <div> and <a> elements with Tailwind utility classes resolved from semantic tokens.

typescript
const html = editor.getHTML();
// <h1>Hello World</h1>
// <div class="p-4 bg-neutral-100 rounded-lg" data-editu-group>
//   <p>Content inside the group...</p>
// </div>

This is the format to use when you need to render editor content outside the editor — in emails, preview pages, or server-side rendered views.


Tailwind Token Mapping ​

Layout blocks store semantic tokens as attributes (e.g., padding: "md", background: "muted"). When the editor renders these blocks — both in the editor view and in getHTML() output — it resolves tokens to Tailwind CSS utility classes via the token registry.

Token Registries ​

RegistryToken TypeExample
EDITU_SPACING_TOKENSPadding"md" → "p-4"
EDITU_BACKGROUND_TOKENSBackground"muted" → "bg-neutral-100"
EDITU_RADIUS_TOKENSBorder radius"lg" → "rounded-lg"
EDITU_HEIGHT_TOKENSSpacer height"lg" → "h-16"
EDITU_DIVIDER_STYLE_TOKENSDivider style"dashed" → "border-dashed"
EDITU_DIVIDER_THICKNESS_TOKENSDivider thickness"thick" → "border-t-4"
EDITU_BUTTON_VARIANT_TOKENSButton variant"primary" → "bg-blue-600 text-white hover:bg-blue-700"

Resolver API ​

Each registry exports a resolver function you can use when rendering layout blocks outside the editor:

typescript
import {
  resolveEdituSpacing,
  resolveEdituBackground,
  resolveEdituRadius,
  resolveEdituSpacerHeight,
  resolveEdituButtonVariant,
} from '@editu/core/tokens';

resolveEdituSpacing('md');            // "p-4"
resolveEdituBackground('accent');     // "bg-blue-50"
resolveEdituRadius('lg');             // "rounded-lg"
resolveEdituSpacerHeight('xl');       // "h-24"
resolveEdituButtonVariant('ghost');   // "bg-transparent text-blue-600 hover:bg-blue-50"

TIP

If your project uses a custom Tailwind theme, you can override the token-to-class mappings by providing your own registry. See Layout Blocks - Token Registry for details.


Working Example: HTML Export with Layout Blocks ​

This example creates an editor with layout blocks, inserts content programmatically, and exports the final HTML:

tsx
import { Editu } from '@editu/react';
import {
  EdituGroupReact,
  EdituColumnsReact,
  EdituColumnReact,
  EdituSpacerReact,
  EdituButtonReact,
} from '@editu/react';
import type { Editor } from '@tiptap/core';

function ExportDemo() {
  const handleCreate = ({ editor }: { editor: Editor }) => {
    // Insert layout blocks programmatically
    editor.commands.setContent({
      type: 'doc',
      content: [
        {
          type: 'group',
          attrs: { padding: 'lg', background: 'muted', borderRadius: 'lg' },
          content: [
            { type: 'heading', attrs: { level: 1 }, content: [{ type: 'text', text: 'Welcome' }] },
            { type: 'paragraph', content: [{ type: 'text', text: 'This is a styled section.' }] },
          ],
        },
        { type: 'spacer', attrs: { height: 'md' } },
        {
          type: 'button',
          attrs: { label: 'Get Started', href: '/docs', variant: 'primary', target: '_self' },
        },
      ],
    });
  };

  const handleExport = (editor: Editor) => {
    const html = editor.getHTML();
    console.log(html);
    // Output includes Tailwind classes:
    // <div class="p-6 bg-neutral-100 rounded-lg" data-editu-group>
    //   <h1>Welcome</h1>
    //   <p>This is a styled section.</p>
    // </div>
    // <div class="h-8" data-editu-spacer></div>
    // <a class="bg-blue-600 text-white ..." href="/docs">Get Started</a>
  };

  return (
    <Editu
      extensions={[
        EdituGroupReact,
        EdituColumnsReact,
        EdituColumnReact,
        EdituSpacerReact,
        EdituButtonReact,
      ]}
      onCreate={handleCreate}
      onUpdate={({ editor }) => handleExport(editor)}
    />
  );
}

WARNING

The HTML output includes Tailwind utility classes. Make sure your rendering environment has Tailwind CSS configured, or the classes won't produce any styles.


When to Use Each Format ​

ScenarioRecommended Format
Save to database, re-import laterJSON (lossless round-trip)
Store in Git, render on static siteMarkdown (human-readable, diffable)
Send as email or render server-sideHTML (ready to display)
Transfer between Editu instancesJSON (exact document structure)
Feed to a CMS or documentation toolMarkdown (wide tool support)

Next Steps ​

Released under the MIT License.