Block-style WYSIWYG editor that outputs clean JSON data instead of HTML markup.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Editor.js provides extensive configuration options to customize editor behavior, appearance, and functionality. The EditorConfig interface allows fine-grained control over every aspect of the editor.
Core settings for initializing the editor.
interface EditorConfig {
/**
* Element where editor will be appended
*/
holder?: string | HTMLElement;
/**
* Deprecated: use holder instead
*/
holderId?: string | HTMLElement;
/**
* Focus editor on first block after initialization
*/
autofocus?: boolean;
/**
* Default tool for new blocks
*/
defaultBlock?: string;
/**
* Deprecated: use defaultBlock instead
*/
initialBlock?: string;
/**
* Placeholder text for empty editor
*/
placeholder?: string | false;
}Usage Examples:
// Basic configuration
const editor = new EditorJS({
holder: 'editorjs', // Element ID
autofocus: true,
defaultBlock: 'paragraph',
placeholder: 'Start writing your story...'
});
// Using DOM element reference
const holderElement = document.getElementById('my-editor');
const editor = new EditorJS({
holder: holderElement,
autofocus: false,
placeholder: false // Disable placeholder
});
// Configuration with multiple options
const editor = new EditorJS({
holder: 'editorjs',
autofocus: true,
defaultBlock: 'header', // New blocks will be headers by default
placeholder: 'Enter your title here...'
});Settings for configuring available tools and their behavior.
interface EditorConfig {
/**
* Map of available tools
*/
tools?: {
[toolName: string]: ToolConstructable | ToolSettings;
};
/**
* Default inline toolbar tools for all blocks
*/
inlineToolbar?: string[] | boolean;
/**
* Common block tunes applied to all blocks
*/
tunes?: string[];
}
interface ToolSettings {
class: ToolConstructable;
config?: ToolConfig;
inlineToolbar?: string[] | boolean;
tunes?: string[];
shortcut?: string;
}Usage Examples:
import Header from '@editorjs/header';
import Paragraph from '@editorjs/paragraph';
import List from '@editorjs/list';
const editor = new EditorJS({
tools: {
// Simple tool registration
header: Header,
// Tool with configuration
paragraph: {
class: Paragraph,
config: {
placeholder: 'Type your paragraph here...'
},
inlineToolbar: ['bold', 'italic', 'link']
},
// Tool with tunes and shortcut
list: {
class: List,
config: {
defaultStyle: 'unordered'
},
tunes: ['textAlignment'],
shortcut: 'CMD+SHIFT+L'
}
},
// Default inline toolbar for all tools
inlineToolbar: ['bold', 'italic', 'link'],
// Common tunes for all blocks
tunes: ['textAlignment', 'anchor']
});Settings for initial content and data handling.
interface EditorConfig {
/**
* Initial data to render in editor
*/
data?: OutputData;
/**
* HTML sanitization configuration
*/
sanitizer?: SanitizerConfig;
}Usage Examples:
// Editor with initial content
const editor = new EditorJS({
data: {
version: "2.30.8",
time: Date.now(),
blocks: [
{
type: "header",
data: {
text: "Welcome to My Blog",
level: 1
}
},
{
type: "paragraph",
data: {
text: "This is the first paragraph of content."
}
}
]
}
});
// Custom sanitization rules
const editor = new EditorJS({
sanitizer: {
b: true,
i: true,
u: true,
s: true,
p: {
class: 'custom-paragraph'
},
a: {
href: true,
target: '_blank'
}
}
});Settings that control editor behavior and functionality.
interface EditorConfig {
/**
* Enable read-only mode
*/
readOnly?: boolean;
/**
* Hide the toolbar completely
*/
hideToolbar?: boolean;
/**
* Minimum height of editor area
*/
minHeight?: number;
/**
* Logging level for debugging
*/
logLevel?: LogLevels;
}
type LogLevels = 'VERBOSE' | 'INFO' | 'WARN' | 'ERROR';Usage Examples:
// Read-only editor for displaying content
const readOnlyEditor = new EditorJS({
holder: 'display-area',
readOnly: true,
hideToolbar: true,
data: existingContent
});
// Editor with custom behavior
const editor = new EditorJS({
holder: 'editorjs',
minHeight: 300, // Minimum 300px height
logLevel: 'WARN', // Only show warnings and errors
hideToolbar: false
});
// Development editor with verbose logging
const devEditor = new EditorJS({
holder: 'dev-editor',
logLevel: 'VERBOSE', // Show all debug information
minHeight: 500
});Callback functions for responding to editor events.
interface EditorConfig {
/**
* Called when editor is fully initialized
*/
onReady?(): void;
/**
* Called when editor content changes
*/
onChange?(api: API, event: BlockMutationEvent | BlockMutationEvent[]): void;
}Usage Examples:
const editor = new EditorJS({
onReady: () => {
console.log('Editor.js is ready to work!');
// Perform initialization tasks
loadDraftFromLocalStorage();
setupAutoSave();
},
onChange: (api, event) => {
console.log('Content changed:', event);
// Auto-save functionality
clearTimeout(autoSaveTimer);
autoSaveTimer = setTimeout(async () => {
const data = await api.saver.save();
localStorage.setItem('editor-content', JSON.stringify(data));
}, 2000);
// Track changes for analytics
if (Array.isArray(event)) {
event.forEach(e => trackChange(e));
} else {
trackChange(event);
}
}
});Settings for multi-language support.
interface EditorConfig {
/**
* Internationalization configuration
*/
i18n?: I18nConfig;
}
interface I18nConfig {
/**
* Current language direction
*/
direction?: 'ltr' | 'rtl';
/**
* Translation messages
*/
messages?: I18nDictionary;
}
interface I18nDictionary {
ui?: Dictionary;
toolNames?: Dictionary;
tools?: Dictionary;
blockTunes?: Dictionary;
}Usage Examples:
// Arabic/RTL configuration
const arabicEditor = new EditorJS({
i18n: {
direction: 'rtl',
messages: {
ui: {
'blockTunes.delete.confirm': 'هل أنت متأكد؟',
'toolbar.toolbox.add': 'إضافة'
},
toolNames: {
'paragraph': 'فقرة',
'header': 'عنوان'
}
}
}
});
// Custom English messages
const customEditor = new EditorJS({
i18n: {
messages: {
ui: {
'blockTunes.delete.confirm': 'Are you sure you want to delete this block?',
'toolbar.toolbox.add': 'Add new block'
},
toolNames: {
'paragraph': 'Text Block',
'header': 'Heading'
}
}
}
});Settings for custom styling and security.
interface EditorConfig {
/**
* Style-related configuration
*/
style?: {
/**
* Nonce for Content Security Policy
*/
nonce?: string;
};
}Usage Examples:
// CSP-compliant configuration
const secureEditor = new EditorJS({
holder: 'secure-editor',
style: {
nonce: 'random-nonce-value-123' // For CSP style-src policy
}
});// Comprehensive editor configuration
const editor = new EditorJS({
// Basic setup
holder: 'editorjs',
autofocus: true,
defaultBlock: 'paragraph',
placeholder: 'Tell your story...',
// Tools configuration
tools: {
header: {
class: Header,
config: {
levels: [1, 2, 3, 4],
defaultLevel: 2
},
inlineToolbar: ['bold', 'italic'],
tunes: ['textAlignment']
},
paragraph: {
class: Paragraph,
config: {
placeholder: 'Enter your text here...'
},
inlineToolbar: true
},
list: {
class: List,
inlineToolbar: true,
tunes: ['textAlignment']
},
quote: {
class: Quote,
config: {
quotePlaceholder: 'Enter a quote',
captionPlaceholder: 'Quote author'
}
}
},
// Global settings
inlineToolbar: ['bold', 'italic', 'link'],
tunes: ['textAlignment'],
// Behavior
readOnly: false,
minHeight: 400,
logLevel: 'WARN',
// Initial content
data: {
blocks: [
{
type: 'header',
data: {
text: 'Welcome!',
level: 1
}
}
]
},
// Event handlers
onReady: () => {
console.log('Editor is ready');
},
onChange: (api, event) => {
console.log('Content changed');
},
// Internationalization
i18n: {
direction: 'ltr',
messages: {
ui: {
'blockTunes.delete.confirm': 'Delete this block?'
}
}
},
// Security
style: {
nonce: generateNonce()
}
});Editor.js validates configuration and provides helpful error messages:
Usage Examples:
try {
const editor = new EditorJS({
holder: 'non-existent-element', // This will throw an error
tools: {
invalidTool: 'not-a-constructor' // This will also cause issues
}
});
} catch (error) {
console.error('Configuration error:', error.message);
// Handle configuration errors gracefully
const fallbackEditor = new EditorJS({
holder: 'fallback-container',
tools: {} // Minimal safe configuration
});
}interface EditorConfig {
holder?: string | HTMLElement;
holderId?: string | HTMLElement;
autofocus?: boolean;
defaultBlock?: string;
initialBlock?: string;
placeholder?: string | false;
sanitizer?: SanitizerConfig;
hideToolbar?: boolean;
tools?: {[toolName: string]: ToolConstructable | ToolSettings};
data?: OutputData;
minHeight?: number;
logLevel?: LogLevels;
readOnly?: boolean;
i18n?: I18nConfig;
onReady?(): void;
onChange?(api: API, event: BlockMutationEvent | BlockMutationEvent[]): void;
inlineToolbar?: string[] | boolean;
tunes?: string[];
style?: { nonce?: string };
}
interface ToolSettings {
class: ToolConstructable;
config?: ToolConfig;
inlineToolbar?: string[] | boolean;
tunes?: string[];
shortcut?: string;
}
interface SanitizerConfig {
[tagName: string]: boolean | {
[attrName: string]: boolean | string;
};
}
type LogLevels = 'VERBOSE' | 'INFO' | 'WARN' | 'ERROR';Install with Tessl CLI
npx tessl i tessl/npm-editorjs--editorjs