React components and hooks for building customizable rich text editors using the Slate framework.
—
React-specific plugin functionality that integrates Slate with React's lifecycle and event system. The plugin system enables Slate editors to work seamlessly with React components and provides React-specific enhancements.
The withReact function enhances a Slate editor with React-specific behaviors, DOM handling, and integration with React's component lifecycle.
/**
* Adds React and DOM specific behaviors to the editor
* @param editor - Base editor to enhance
* @param clipboardFormatKey - Key for clipboard data format (default: 'x-slate-fragment')
* @returns Enhanced editor with React capabilities
*/
function withReact<T extends BaseEditor>(
editor: T,
clipboardFormatKey?: string
): T & ReactEditor;Usage Example:
import React, { useMemo } from 'react';
import { createEditor } from 'slate';
import { withReact } from 'slate-react';
const MyEditor = () => {
// Apply React plugin to base editor
const editor = useMemo(() => withReact(createEditor()), []);
// Editor now has React-specific capabilities
return (
<Slate editor={editor} initialValue={initialValue}>
<Editable />
</Slate>
);
};
// With custom clipboard format
const EditorWithCustomClipboard = () => {
const editor = useMemo(() =>
withReact(createEditor(), 'my-custom-format'),
[]
);
return (
<Slate editor={editor} initialValue={initialValue}>
<Editable />
</Slate>
);
};The ReactEditor interface extends DOMEditor from slate-dom and provides React-specific editor methods and capabilities.
/**
* React and DOM-specific editor interface
* Extends DOMEditor with React-specific functionality
*/
interface ReactEditor extends DOMEditor {
/**
* Get the chunk size for performance optimization
* @param node - Ancestor node to get chunk size for
* @returns Chunk size or null if not chunked
*/
getChunkSize(node: Ancestor): number | null;
}The ReactEditor interface inherits all methods from DOMEditor, including:
Usage Example:
import React from 'react';
import { ReactEditor } from 'slate-react';
import { useSlateStatic } from 'slate-react';
const CustomComponent = () => {
const editor = useSlateStatic() as ReactEditor;
const handleClick = (element: Element) => {
// Use ReactEditor-specific methods
const domElement = ReactEditor.toDOMNode(editor, element);
const slateNode = ReactEditor.toSlateNode(editor, domElement);
// Check chunk size for performance optimization
const chunkSize = editor.getChunkSize(element);
if (chunkSize) {
console.log(`Element is chunked with size: ${chunkSize}`);
}
};
return (
<button onClick={() => handleClick(someElement)}>
Process Element
</button>
);
};The withReact plugin integrates Slate with React's component lifecycle:
React-specific DOM handling and integration:
// DOM element mapping (inherited from DOMEditor)
const domElement = ReactEditor.toDOMNode(editor, slateNode);
const slateNode = ReactEditor.toSlateNode(editor, domElement);
// Range operations
const domRange = ReactEditor.toDOMRange(editor, slateRange);
const slateRange = ReactEditor.toSlateRange(editor, domRange);
// Selection handling
const domSelection = ReactEditor.toDOMSelection(editor, slateSelection);
const slateSelection = ReactEditor.toSlateSelection(editor, domSelection);Enhanced clipboard functionality with React-specific handling:
import { withReact } from 'slate-react';
// Default clipboard format
const editor = withReact(createEditor()); // Uses 'x-slate-fragment'
// Custom clipboard format
const customEditor = withReact(createEditor(), 'my-app-fragment');The plugin integrates with React's event system for:
Usage Example:
import React from 'react';
import { Editable } from 'slate-react';
const EditorWithEvents = () => {
return (
<Editable
onKeyDown={(event) => {
// React synthetic event integration
if (event.key === 'Enter') {
event.preventDefault();
// Handle custom enter behavior
}
}}
onDOMBeforeInput={(event) => {
// Native DOM event integration
console.log('Native input event:', event);
}}
/>
);
};The withReact plugin can be combined with other Slate plugins:
import React, { useMemo } from 'react';
import { createEditor } from 'slate';
import { withReact } from 'slate-react';
import { withHistory } from 'slate-history';
const EditorWithPlugins = () => {
const editor = useMemo(() => {
// Apply plugins in correct order
return withReact(withHistory(createEditor()));
}, []);
return (
<Slate editor={editor} initialValue={initialValue}>
<Editable />
</Slate>
);
};You can create custom plugins that work with the React plugin:
import { Editor, Transforms } from 'slate';
import { ReactEditor } from 'slate-react';
// Custom plugin that adds React-aware functionality
const withCustomReactFeature = <T extends ReactEditor>(editor: T): T => {
const { insertText } = editor;
editor.insertText = (text: string) => {
// Custom logic that works with React
if (text === '@') {
// Trigger React component for mentions
Transforms.insertNodes(editor, {
type: 'mention',
character: '@',
children: [{ text: '' }]
});
return;
}
insertText(text);
};
return editor;
};
// Usage with React plugin
const editor = useMemo(() =>
withCustomReactFeature(withReact(createEditor())),
[]
);Proper TypeScript integration with custom editor types:
import { BaseEditor } from 'slate';
import { ReactEditor } from 'slate-react';
import { HistoryEditor } from 'slate-history';
// Extend editor type for TypeScript
type CustomEditor = BaseEditor & ReactEditor & HistoryEditor & {
customMethod(): void;
};
// Declare module for Slate TypeScript integration
declare module 'slate' {
interface CustomTypes {
Editor: CustomEditor;
Element: CustomElement;
Text: CustomText;
}
}
const withCustom = <T extends ReactEditor>(editor: T): T & { customMethod(): void } => {
editor.customMethod = () => {
console.log('Custom method called');
};
return editor as T & { customMethod(): void };
};Apply plugins in the correct order for optimal functionality:
// Recommended plugin order:
// 1. Core functionality plugins (history, etc.)
// 2. Custom business logic plugins
// 3. React plugin last
const editor = withReact(
withCustomLogic(
withHistory(
createEditor()
)
)
);Implement proper error handling in custom plugins:
const withErrorHandling = <T extends ReactEditor>(editor: T): T => {
const { insertNode } = editor;
editor.insertNode = (node) => {
try {
insertNode(node);
} catch (error) {
console.error('Error inserting node:', error);
// Fallback behavior
}
};
return editor;
};Consider performance implications when creating custom plugins:
const withPerformantPlugin = <T extends ReactEditor>(editor: T): T => {
// Cache expensive operations
const cache = new WeakMap();
editor.customOperation = (node) => {
if (cache.has(node)) {
return cache.get(node);
}
const result = expensiveOperation(node);
cache.set(node, result);
return result;
};
return editor;
};Install with Tessl CLI
npx tessl i tessl/npm-slate-react