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
The editor instance with plugins that define HTML deserializers
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: '...' } }
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