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

render-functions.mddocs/

Render Functions

Customizable render functions for controlling how editor content is displayed. These functions provide complete control over the visual representation of editor content and are essential for creating custom editor UI.

Capabilities

Element Rendering

renderElement

Custom function for rendering Slate elements as React components.

/**
 * Custom element renderer props passed to renderElement
 */
interface RenderElementProps {
  /** React children to render inside the element */
  children: any;
  /** The Slate element being rendered */
  element: Element;  
  /** Required DOM attributes for proper Slate functionality */
  attributes: {
    'data-slate-node': 'element';
    'data-slate-inline'?: true;
    'data-slate-void'?: true;
    dir?: 'rtl';
    ref: any;
  };
}

/**
 * Type for custom element render function
 * @param props - Render element props
 * @returns JSX element representing the rendered element
 */
type RenderElementFunction = (props: RenderElementProps) => JSX.Element;

Usage Example:

import React from 'react';
import { Editable, RenderElementProps } from 'slate-react';

const renderElement = ({ attributes, children, element }: RenderElementProps) => {
  switch (element.type) {
    case 'paragraph':
      return <p {...attributes}>{children}</p>;
    case 'heading-one':
      return <h1 {...attributes}>{children}</h1>;
    case 'heading-two':
      return <h2 {...attributes}>{children}</h2>;
    case 'block-quote':
      return <blockquote {...attributes}>{children}</blockquote>;
    case 'list-item':
      return <li {...attributes}>{children}</li>;
    case 'bulleted-list':
      return <ul {...attributes}>{children}</ul>;
    case 'numbered-list':
      return <ol {...attributes}>{children}</ol>;
    case 'link':
      return (
        <a {...attributes} href={element.url}>
          {children}
        </a>
      );
    default:
      return <div {...attributes}>{children}</div>;
  }
};

const MyEditor = () => (
  <Editable renderElement={renderElement} />
);

Leaf Rendering

renderLeaf

Custom function for rendering Slate text leaves with formatting.

/**
 * Custom leaf renderer props passed to renderLeaf
 */
interface RenderLeafProps {
  /** React children to render inside the leaf */
  children: any;
  /** The Slate leaf being rendered (same as text) */
  leaf: Text;
  /** The Slate text node being rendered */
  text: Text;
  /** Required DOM attributes for proper Slate functionality */
  attributes: {
    'data-slate-leaf': true;
    ref: any;
  };
  /** Position information for the leaf (optional) */
  leafPosition?: LeafPosition;
}

/**
 * Type for custom leaf render function
 * @param props - Render leaf props
 * @returns JSX element representing the rendered leaf
 */
type RenderLeafFunction = (props: RenderLeafProps) => JSX.Element;

Usage Example:

import React from 'react';
import { Editable, RenderLeafProps } from 'slate-react';

const renderLeaf = ({ attributes, children, leaf }: RenderLeafProps) => {
  if (leaf.bold) {
    children = <strong>{children}</strong>;
  }
  
  if (leaf.italic) {
    children = <em>{children}</em>;
  }
  
  if (leaf.underline) {
    children = <u>{children}</u>;
  }
  
  if (leaf.strikethrough) {
    children = <del>{children}</del>;
  }
  
  if (leaf.code) {
    children = <code>{children}</code>;
  }
  
  if (leaf.highlight) {
    children = (
      <span style={{ backgroundColor: 'yellow' }}>
        {children}
      </span>
    );
  }
  
  return <span {...attributes}>{children}</span>;
};

const MyEditor = () => (
  <Editable renderLeaf={renderLeaf} />
);

Text Rendering

renderText

Custom function for rendering individual text nodes. Rarely needed unless you need very specific text handling.

/**
 * Custom text renderer props passed to renderText
 */
interface RenderTextProps {
  /** The Slate text node being rendered */
  text: Text;
  /** React children to render */
  children: any;
  /** Required DOM attributes for proper Slate functionality */
  attributes: {
    'data-slate-text': true;
    ref: any;
  };
}

/**
 * Type for custom text render function
 * @param props - Render text props
 * @returns JSX element representing the rendered text
 */
type RenderTextFunction = (props: RenderTextProps) => JSX.Element;

Usage Example:

import React from 'react';
import { Editable, RenderTextProps } from 'slate-react';

const renderText = ({ attributes, children, text }: RenderTextProps) => {
  // Custom text processing - e.g., syntax highlighting
  if (text.text.includes('TODO:')) {
    return (
      <span {...attributes} style={{ color: 'red', fontWeight: 'bold' }}>
        {children}
      </span>
    );
  }
  
  return <span {...attributes}>{children}</span>;
};

const MyEditor = () => (
  <Editable renderText={renderText} />
);

Placeholder Rendering

renderPlaceholder

Custom function for rendering the editor placeholder when it's empty.

/**
 * Custom placeholder renderer props passed to renderPlaceholder
 */
interface RenderPlaceholderProps {
  /** React children to render (the placeholder text) */
  children: any;
  /** Required DOM attributes for proper Slate functionality */
  attributes: {
    'data-slate-placeholder': boolean;
    dir?: 'rtl';
    contentEditable: boolean;
    ref: React.RefCallback<any>;
    style: React.CSSProperties;
  };
}

/**
 * Type for custom placeholder render function
 * @param props - Render placeholder props
 * @returns JSX element representing the rendered placeholder
 */
type RenderPlaceholderFunction = (props: RenderPlaceholderProps) => JSX.Element;

Usage Example:

import React from 'react';
import { Editable, RenderPlaceholderProps } from 'slate-react';

const renderPlaceholder = ({ attributes, children }: RenderPlaceholderProps) => (
  <div 
    {...attributes}
    style={{
      ...attributes.style,
      color: '#aaa',
      fontStyle: 'italic',
      fontSize: '14px'
    }}
  >
    {children}
  </div>
);

const MyEditor = () => (
  <Editable 
    placeholder="Type something amazing..."
    renderPlaceholder={renderPlaceholder}
  />
);

Chunk Rendering (Performance)

renderChunk

Custom function for rendering chunks in performance-optimized scenarios with large documents.

/**
 * Custom chunk renderer props passed to renderChunk
 */
interface RenderChunkProps {
  /** Whether this is the highest-level chunk */
  highest: boolean;
  /** Whether this is the lowest-level chunk */
  lowest: boolean;
  /** React children to render inside the chunk */
  children: any;
  /** Required DOM attributes for proper Slate functionality */
  attributes: {
    'data-slate-chunk': true;
    ref: any;
  };
}

/**
 * Type for custom chunk render function
 * @param props - Render chunk props
 * @returns JSX element representing the rendered chunk
 */
type RenderChunkFunction = (props: RenderChunkProps) => JSX.Element;

Usage Example:

import React from 'react';
import { Editable, RenderChunkProps } from 'slate-react';

const renderChunk = ({ attributes, children, highest, lowest }: RenderChunkProps) => {
  // Apply different styling based on chunk level
  const className = highest ? 'chunk-highest' : lowest ? 'chunk-lowest' : 'chunk-middle';
  
  return (
    <div {...attributes} className={className}>
      {children}
    </div>
  );
};

const LargeDocumentEditor = () => (
  <Editable renderChunk={renderChunk} />
);

Advanced Rendering Patterns

Conditional Rendering

Render different components based on element properties or editor state:

import React from 'react';
import { useSelected, useFocused, RenderElementProps } from 'slate-react';

const ConditionalElement = ({ attributes, children, element }: RenderElementProps) => {
  const selected = useSelected();
  const focused = useFocused();
  
  const isActive = selected && focused;
  
  switch (element.type) {
    case 'image':
      return (
        <div {...attributes}>
          <img 
            src={element.url} 
            alt={element.alt}
            style={{
              border: isActive ? '2px solid blue' : 'none',
              maxWidth: '100%'
            }}
          />
          {children}
        </div>
      );
    case 'video':
      return (
        <div {...attributes}>
          <video 
            src={element.url} 
            controls
            style={{
              border: isActive ? '2px solid blue' : 'none',
              maxWidth: '100%'
            }}
          />
          {children}
        </div>
      );
    default:
      return <div {...attributes}>{children}</div>;
  }
};

Void Elements

Render void elements (elements that don't contain editable text):

import React from 'react';
import { RenderElementProps } from 'slate-react';

const VoidElement = ({ attributes, children, element }: RenderElementProps) => {
  switch (element.type) {
    case 'image':
      return (
        <div {...attributes}>
          <div contentEditable={false}>
            <img src={element.url} alt={element.alt} />
          </div>
          {children}
        </div>
      );
    case 'horizontal-rule':
      return (
        <div {...attributes}>
          <div contentEditable={false}>
            <hr />
          </div>
          {children}
        </div>
      );
    default:
      return <div {...attributes}>{children}</div>;
  }
};

Interactive Elements

Create interactive elements with event handlers:

import React from 'react';
import { useSlateStatic, RenderElementProps } from 'slate-react';
import { Transforms } from 'slate';

const InteractiveElement = ({ attributes, children, element }: RenderElementProps) => {
  const editor = useSlateStatic();
  
  const handleButtonClick = () => {
    const path = ReactEditor.findPath(editor, element);
    Transforms.setNodes(
      editor,
      { clicked: !element.clicked },
      { at: path }
    );
  };
  
  if (element.type === 'button') {
    return (
      <div {...attributes}>
        <button 
          contentEditable={false}
          onClick={handleButtonClick}
          style={{
            backgroundColor: element.clicked ? 'green' : 'gray',
            color: 'white',
            border: 'none',
            padding: '8px 16px',
            cursor: 'pointer'
          }}
        >
          {element.text || 'Click me'}
        </button>
        {children}
      </div>
    );
  }
  
  return <div {...attributes}>{children}</div>;
};

Complex Leaf Formatting

Handle complex text formatting combinations:

import React from 'react';
import { RenderLeafProps } from 'slate-react';

const ComplexLeaf = ({ attributes, children, leaf }: RenderLeafProps) => {
  let element = children;
  
  // Apply formatting in specific order
  if (leaf.code) {
    element = <code>{element}</code>;
  }
  
  if (leaf.bold) {
    element = <strong>{element}</strong>;
  }
  
  if (leaf.italic) {
    element = <em>{element}</em>;
  }
  
  if (leaf.underline) {
    element = <u>{element}</u>;
  }
  
  if (leaf.strikethrough) {
    element = <del>{element}</del>;
  }
  
  if (leaf.highlight) {
    element = (
      <span style={{ backgroundColor: leaf.highlightColor || 'yellow' }}>
        {element}
      </span>
    );
  }
  
  if (leaf.fontSize) {
    element = (
      <span style={{ fontSize: `${leaf.fontSize}px` }}>
        {element}
      </span>
    );
  }
  
  if (leaf.color) {
    element = (
      <span style={{ color: leaf.color }}>
        {element}
      </span>
    );
  }
  
  return <span {...attributes}>{element}</span>;
};

Render Function Best Practices

Always Spread Attributes

Always spread the attributes prop on your root element:

// ✅ Correct
const MyElement = ({ attributes, children }: RenderElementProps) => (
  <div {...attributes}>{children}</div>
);

// ❌ Incorrect - missing attributes
const BadElement = ({ children }: RenderElementProps) => (
  <div>{children}</div>
);

Preserve Children

Always render the children prop:

// ✅ Correct
const MyElement = ({ attributes, children }: RenderElementProps) => (
  <div {...attributes}>
    <h1>My Header</h1>
    {children}
  </div>
);

// ❌ Incorrect - missing children
const BadElement = ({ attributes }: RenderElementProps) => (
  <div {...attributes}>
    <h1>My Header</h1>
  </div>
);

Use contentEditable={false} for Non-Editable Areas

For void elements or interactive components:

const ImageElement = ({ attributes, children, element }: RenderElementProps) => (
  <div {...attributes}>
    <div contentEditable={false}>
      <img src={element.url} alt={element.alt} />
    </div>
    {children}
  </div>
);

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