CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-slate-react

React components and hooks for building customizable rich text editors using the Slate framework.

Pending
Overview
Eval results
Files

plugin-system.mddocs/

Plugin System

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.

Capabilities

withReact Plugin

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>
  );
};

ReactEditor Interface

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:

  • DOM manipulation methods
  • Event handling utilities
  • Selection and range operations
  • Node-to-DOM mapping functions

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>
  );
};

Plugin Features

React Lifecycle Integration

The withReact plugin integrates Slate with React's component lifecycle:

  • Component Updates: Synchronizes editor state with React component updates
  • Event Handling: Integrates DOM events with React's synthetic event system
  • Context Propagation: Ensures proper context propagation throughout the editor tree
  • Performance Optimization: Implements React-specific performance optimizations

DOM Integration

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);

Clipboard Integration

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');

Event System Integration

The plugin integrates with React's event system for:

  • Keyboard Events: Handles keyboard shortcuts and input
  • Mouse Events: Manages selection and interaction
  • Focus Events: Tracks editor focus state
  • Composition Events: Handles IME input and composition

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);
      }}
    />
  );
};

Advanced Plugin Patterns

Combining Multiple Plugins

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>
  );
};

Custom Plugin Creation

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())), 
  []
);

TypeScript Integration

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 };
};

Plugin System Best Practices

Plugin Order

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()
    )
  )
);

Error Handling

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;
};

Performance Considerations

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

docs

core-components.md

index.md

performance-optimization.md

plugin-system.md

react-hooks.md

render-functions.md

tile.json