Skip to main content

Overview

The deserializeHTML function converts HTML into Yoopta editor blocks. It parses HTML elements using plugin-defined deserializers and creates properly structured YooptaBlockData objects.

Usage

import { deserializeHTML } from '@yoopta/editor';

function ImportHTMLButton() {
  const editor = useYooptaEditor();

  const handleImport = () => {
    const htmlString = '<h1>Hello</h1><p>World</p>';
    const blocks = deserializeHTML(editor, htmlElement);
    
    // Set as editor content
    const content = {};
    blocks.forEach((block, i) => {
      block.meta.order = i;
      content[block.id] = block;
    });
    
    editor.setEditorValue(content);
  };

  return <button onClick={handleImport}>Import HTML</button>;
}

Signature

function deserializeHTML(
  editor: YooEditor,
  html: HTMLElement
): YooptaBlockData[]

Parameters

editor
YooEditor
required
The editor instance with plugins that define HTML deserializers
html
HTMLElement
required
The HTML element to parse. Can be any DOM element (typically body or a container).

Returns

YooptaBlockData[] - Array of deserialized block data objects

How It Works

Plugin Deserializers

Plugins define which HTML elements they can parse:
const ParagraphPlugin = {
  parsers: {
    html: {
      deserialize: {
        nodeNames: ['P'],  // Match <p> tags
        parse: (element, editor) => {
          // Return SlateElement or YooptaBlockData
        },
      },
    },
  },
};

Element Matching

The deserializer matches HTML elements by tag name:
  • <P> → Paragraph plugin
  • <H1> → HeadingOne plugin
  • <UL> → BulletedList plugin
  • <OL> → NumberedList plugin
  • etc.

Mark Conversion

HTML formatting tags are converted to text marks:
  • <strong>, <b>{ bold: true }
  • <em>, <i>{ italic: true }
  • <u>{ underline: true }
  • <s>{ strike: true }
  • <code>{ code: true }
  • <mark>{ highlight: { color: '...' } }

Metadata Extraction

Data attributes preserve block metadata:
<p data-meta-align="center" data-meta-depth="1">
  Content
</p>

Examples

Parse HTML String

function ParseHTMLString() {
  const editor = useYooptaEditor();

  const handleParse = () => {
    const htmlString = `
      <h1>My Document</h1>
      <p>This is a paragraph.</p>
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
    `;

    // Parse string into DOM
    const parser = new DOMParser();
    const doc = parser.parseFromString(htmlString, 'text/html');
    
    // Deserialize
    const blocks = deserializeHTML(editor, doc.body);
    
    // Convert to content value
    const content = {};
    blocks.forEach((block, i) => {
      block.meta.order = i;
      content[block.id] = block;
    });
    
    editor.setEditorValue(content);
  };

  return <button onClick={handleParse}>Parse HTML</button>;
}

Import from File

function ImportHTMLFile() {
  const editor = useYooptaEditor();

  const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    const text = await file.text();
    const parser = new DOMParser();
    const doc = parser.parseFromString(text, 'text/html');
    
    const blocks = deserializeHTML(editor, doc.body);
    
    const content = {};
    blocks.forEach((block, i) => {
      block.meta.order = i;
      content[block.id] = block;
    });
    
    editor.setEditorValue(content);
  };

  return (
    <input 
      type="file" 
      accept=".html" 
      onChange={handleFileUpload}
    />
  );
}

Paste HTML Content

function PasteHandler() {
  const editor = useYooptaEditor();

  useEffect(() => {
    const handlePaste = (e: ClipboardEvent) => {
      const html = e.clipboardData?.getData('text/html');
      if (!html) return;

      e.preventDefault();
      
      const parser = new DOMParser();
      const doc = parser.parseFromString(html, 'text/html');
      const blocks = deserializeHTML(editor, doc.body);
      
      // Insert at current position
      blocks.forEach((block) => {
        editor.insertBlock({ block });
      });
    };

    document.addEventListener('paste', handlePaste);
    return () => document.removeEventListener('paste', handlePaste);
  }, [editor]);

  return null;
}

Import from URL

function ImportFromURL() {
  const editor = useYooptaEditor();
  const [url, setUrl] = useState('');
  const [loading, setLoading] = useState(false);

  const handleImport = async () => {
    setLoading(true);
    try {
      const response = await fetch(url);
      const html = await response.text();
      
      const parser = new DOMParser();
      const doc = parser.parseFromString(html, 'text/html');
      const blocks = deserializeHTML(editor, doc.body);
      
      const content = {};
      blocks.forEach((block, i) => {
        block.meta.order = i;
        content[block.id] = block;
      });
      
      editor.setEditorValue(content);
      alert('Imported successfully!');
    } catch (error) {
      alert('Failed to import');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <input
        type="url"
        value={url}
        onChange={(e) => setUrl(e.target.value)}
        placeholder="Enter URL..."
      />
      <button onClick={handleImport} disabled={loading}>
        {loading ? 'Importing...' : 'Import'}
      </button>
    </div>
  );
}

Merge Imported Content

function MergeHTMLContent() {
  const editor = useYooptaEditor();

  const handleMerge = () => {
    const htmlString = '<h2>New Section</h2><p>Additional content</p>';
    
    const parser = new DOMParser();
    const doc = parser.parseFromString(htmlString, 'text/html');
    const blocks = deserializeHTML(editor, doc.body);
    
    // Get current content
    const currentContent = { ...editor.children };
    const currentBlockCount = Object.keys(currentContent).length;
    
    // Add new blocks
    blocks.forEach((block, i) => {
      block.meta.order = currentBlockCount + i;
      currentContent[block.id] = block;
    });
    
    editor.setEditorValue(currentContent);
  };

  return <button onClick={handleMerge}>Merge Content</button>;
}

Convert External Content

function ConvertArticle() {
  const editor = useYooptaEditor();

  const handleConvert = async () => {
    // Fetch article from CMS or external source
    const article = await fetchArticle();
    
    // Build HTML structure
    const htmlString = `
      <h1>${article.title}</h1>
      <p><em>${article.excerpt}</em></p>
      ${article.content}
    `;
    
    const parser = new DOMParser();
    const doc = parser.parseFromString(htmlString, 'text/html');
    const blocks = deserializeHTML(editor, doc.body);
    
    const content = {};
    blocks.forEach((block, i) => {
      block.meta.order = i;
      content[block.id] = block;
    });
    
    editor.setEditorValue(content);
  };

  return <button onClick={handleConvert}>Convert Article</button>;
}

Plugin Deserializer Example

Define how a plugin parses HTML:
const HeadingOnePlugin = createYooptaPlugin({
  type: 'HeadingOne',
  elements: {
    'heading-one': {
      render: HeadingOneRender,
    },
  },
  parsers: {
    html: {
      deserialize: {
        nodeNames: ['H1'],
        parse(element, editor) {
          const align = element.getAttribute('data-meta-align');
          const depth = parseInt(element.getAttribute('data-meta-depth') || '0');
          
          return {
            id: generateId(),
            type: 'HeadingOne',
            value: [
              {
                id: generateId(),
                type: 'heading-one',
                children: [{ text: element.textContent || '' }],
                props: { nodeType: 'block' },
              },
            ],
            meta: {
              order: 0,
              depth,
              align,
            },
          };
        },
      },
    },
  },
});

Special Cases

Yoopta Clipboard Data

When HTML contains Yoopta’s embedded JSON (from copy/paste), it’s used for lossless import:
<body 
  id="yoopta-clipboard" 
  data-yoopta-json="{...}"
>
  <!-- Content -->
</body>
The JSON data is extracted and parsed directly, preserving all metadata.

Multiple Plugin Matches

If multiple plugins match the same HTML element, all are attempted and the first successful parse is used.

Inline Elements

Inline elements (like links) are handled by their respective plugins:
<p>Check out <a href="https://example.com">this link</a></p>
The link plugin deserializes the <a> tag as an inline element within the paragraph.

Cache Management

The deserializer caches plugin mappings for performance. Clear cache when plugins change:
import { clearDeserializeCache } from '@yoopta/editor';

// After updating plugins
clearDeserializeCache(editor);

Use Cases

  • Import Content: Load HTML from external sources
  • Paste Support: Handle rich text paste from clipboard
  • Migration: Convert content from other editors
  • CMS Integration: Import from content management systems
  • Email Import: Parse email content into editor
  • Web Scraping: Convert web pages to editable content
  • Backup Restore: Restore content from HTML backups

See Also

Build docs developers (and LLMs) love