A React framework for building text editors with immutable data models.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Functions for converting between Draft.js content and other formats including raw objects for serialization and HTML for import/export.
Functions for converting between Draft.js ContentState and plain JavaScript objects suitable for JSON serialization.
Converts a ContentState to a raw JavaScript object that can be serialized to JSON.
/**
* Convert ContentState to raw object for serialization
* @param contentState - ContentState to convert
* @returns Raw state object suitable for JSON serialization
*/
function convertToRaw(contentState: ContentState): RawDraftContentState;Converts a raw JavaScript object back to a ContentState.
/**
* Convert raw object to ContentState
* @param rawState - Raw state object from JSON deserialization
* @returns ContentState instance
*/
function convertFromRaw(rawState: RawDraftContentState): ContentState;Usage Examples:
import { convertToRaw, convertFromRaw, EditorState } from "draft-js";
// Save editor content to storage
function saveContent(editorState) {
const contentState = editorState.getCurrentContent();
const rawContentState = convertToRaw(contentState);
// Save to localStorage
localStorage.setItem('draft-content', JSON.stringify(rawContentState));
// Or send to server
fetch('/api/save-content', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: rawContentState })
});
}
// Load editor content from storage
function loadContent() {
const rawContentState = JSON.parse(
localStorage.getItem('draft-content') || '{"blocks":[],"entityMap":{}}'
);
const contentState = convertFromRaw(rawContentState);
return EditorState.createWithContent(contentState);
}
// Working with raw content
function analyzeRawContent(rawContentState) {
console.log('Number of blocks:', rawContentState.blocks.length);
console.log('Number of entities:', Object.keys(rawContentState.entityMap).length);
rawContentState.blocks.forEach((block, index) => {
console.log(`Block ${index}: ${block.type} - "${block.text}"`);
});
}
// Modify raw content
function addPrefixToRawContent(rawContentState, prefix) {
return {
...rawContentState,
blocks: rawContentState.blocks.map((block, index) => {
if (index === 0) {
return {
...block,
text: prefix + block.text
};
}
return block;
})
};
}Function for converting HTML strings to Draft.js content blocks.
Converts HTML string to Draft.js content blocks and entities.
/**
* Convert HTML string to Draft.js content blocks
* @param html - HTML string to convert
* @param getSafeBodyFromHTML - Optional function to sanitize HTML
* @param blockRenderMap - Optional block render map for custom elements
* @returns Object containing content blocks and entity map
*/
function convertFromHTML(
html: string,
getSafeBodyFromHTML?: (html: string) => HTMLElement,
blockRenderMap?: DraftBlockRenderMap
): {
contentBlocks: Array<ContentBlock>,
entityMap: any
};Usage Examples:
import { convertFromHTML, ContentState, EditorState } from "draft-js";
// Convert HTML to editor state
function htmlToEditorState(html) {
const { contentBlocks, entityMap } = convertFromHTML(html);
if (contentBlocks) {
const contentState = ContentState.createFromBlockArray(
contentBlocks,
entityMap
);
return EditorState.createWithContent(contentState);
}
return EditorState.createEmpty();
}
// Import HTML content
function importHtmlContent(htmlString) {
// Basic HTML import
const editorState = htmlToEditorState(htmlString);
return editorState;
}
// Import with custom sanitization
function importSanitizedHtml(htmlString) {
const getSafeBodyFromHTML = (html) => {
// Create temporary DOM element
const doc = new DOMParser().parseFromString(html, 'text/html');
// Remove dangerous elements
const scripts = doc.querySelectorAll('script');
scripts.forEach(script => script.remove());
return doc.body;
};
const { contentBlocks, entityMap } = convertFromHTML(
htmlString,
getSafeBodyFromHTML
);
if (contentBlocks) {
const contentState = ContentState.createFromBlockArray(
contentBlocks,
entityMap
);
return EditorState.createWithContent(contentState);
}
return EditorState.createEmpty();
}
// Handle paste from HTML
function handlePastedText(text, html, editorState) {
if (html) {
const { contentBlocks, entityMap } = convertFromHTML(html);
if (contentBlocks) {
const contentState = editorState.getCurrentContent();
const selection = editorState.getSelection();
// Create fragment from pasted content
const pastedContentState = ContentState.createFromBlockArray(
contentBlocks,
entityMap
);
const fragment = pastedContentState.getBlockMap();
// Insert fragment
const newContentState = Modifier.replaceWithFragment(
contentState,
selection,
fragment
);
const newEditorState = EditorState.push(
editorState,
newContentState,
'insert-fragment'
);
return newEditorState;
}
}
return null; // Fall back to default handling
}Utility functions for exporting Draft.js content to various formats.
/**
* Export utilities for converting Draft.js content to other formats
*/
// Export to plain text
function exportToPlainText(contentState: ContentState, delimiter?: string): string {
return contentState.getPlainText(delimiter || '\n');
}
// Export to HTML (basic implementation)
function exportToHTML(contentState: ContentState): string {
const blocks = contentState.getBlocksAsArray();
return blocks.map(block => {
const text = block.getText();
const type = block.getType();
switch (type) {
case 'header-one':
return `<h1>${text}</h1>`;
case 'header-two':
return `<h2>${text}</h2>`;
case 'header-three':
return `<h3>${text}</h3>`;
case 'blockquote':
return `<blockquote>${text}</blockquote>`;
case 'code-block':
return `<pre><code>${text}</code></pre>`;
case 'unordered-list-item':
return `<li>${text}</li>`;
case 'ordered-list-item':
return `<li>${text}</li>`;
default:
return `<p>${text}</p>`;
}
}).join('\n');
}
// Export with inline styles and entities
function exportToRichHTML(contentState: ContentState): string {
const blocks = contentState.getBlocksAsArray();
return blocks.map(block => {
let html = '';
const text = block.getText();
const type = block.getType();
const characterList = block.getCharacterList();
// Process character by character for inline styles
for (let i = 0; i < text.length; i++) {
const char = text[i];
const metadata = characterList.get(i);
const styles = metadata.getStyle();
const entityKey = metadata.getEntity();
let styledChar = char;
// Apply inline styles
if (styles.has('BOLD')) {
styledChar = `<strong>${styledChar}</strong>`;
}
if (styles.has('ITALIC')) {
styledChar = `<em>${styledChar}</em>`;
}
if (styles.has('UNDERLINE')) {
styledChar = `<u>${styledChar}</u>`;
}
// Apply entities
if (entityKey) {
const entity = contentState.getEntity(entityKey);
if (entity.getType() === 'LINK') {
const { url } = entity.getData();
styledChar = `<a href="${url}">${styledChar}</a>`;
}
}
html += styledChar;
}
// Wrap in block element
switch (type) {
case 'header-one':
return `<h1>${html}</h1>`;
case 'header-two':
return `<h2>${html}</h2>`;
case 'header-three':
return `<h3>${html}</h3>`;
case 'blockquote':
return `<blockquote>${html}</blockquote>`;
case 'code-block':
return `<pre><code>${html}</code></pre>`;
default:
return `<p>${html}</p>`;
}
}).join('\n');
}Complete Export Example:
import { convertToRaw, EditorState } from "draft-js";
// Multi-format export utility
class ContentExporter {
static exportAs(editorState, format) {
const contentState = editorState.getCurrentContent();
switch (format) {
case 'raw':
return convertToRaw(contentState);
case 'json':
return JSON.stringify(convertToRaw(contentState), null, 2);
case 'plain':
return contentState.getPlainText('\n');
case 'html':
return this.exportToHTML(contentState);
case 'markdown':
return this.exportToMarkdown(contentState);
default:
throw new Error(`Unsupported export format: ${format}`);
}
}
static exportToHTML(contentState) {
// Implementation for HTML export with styles and entities
return exportToRichHTML(contentState);
}
static exportToMarkdown(contentState) {
const blocks = contentState.getBlocksAsArray();
return blocks.map(block => {
const text = block.getText();
const type = block.getType();
switch (type) {
case 'header-one':
return `# ${text}`;
case 'header-two':
return `## ${text}`;
case 'header-three':
return `### ${text}`;
case 'blockquote':
return `> ${text}`;
case 'code-block':
return `\`\`\`\n${text}\n\`\`\``;
case 'unordered-list-item':
return `- ${text}`;
case 'ordered-list-item':
return `1. ${text}`;
default:
return text;
}
}).join('\n\n');
}
}
// Usage
function exportContent(editorState, format) {
try {
const exported = ContentExporter.exportAs(editorState, format);
console.log(`Exported as ${format}:`, exported);
return exported;
} catch (error) {
console.error('Export failed:', error);
return null;
}
}// Raw content state structure
interface RawDraftContentState {
blocks: Array<RawDraftContentBlock>;
entityMap: {[key: string]: RawDraftEntity};
}
// Raw content block
interface RawDraftContentBlock {
key: string;
type: DraftBlockType;
text: string;
characterList: Array<RawDraftCharacterMetadata>;
depth: number;
data?: {[key: string]: any};
}
// Raw entity
interface RawDraftEntity {
type: DraftEntityType;
mutability: DraftEntityMutability;
data: {[key: string]: any};
}
// Raw character metadata
interface RawDraftCharacterMetadata {
style: Array<string>;
entity: ?string;
}
// HTML conversion result
interface HTMLConversionResult {
contentBlocks: Array<ContentBlock>;
entityMap: any;
}
// Block render map for HTML conversion
type DraftBlockRenderMap = Immutable.Map<DraftBlockType, DraftBlockRenderConfig>;
interface DraftBlockRenderConfig {
element: string;
wrapper?: React.ComponentType<any>;
aliasedElements?: Array<string>;
}Install with Tessl CLI
npx tessl i tessl/npm-draft-js