SlateJS can render arbitrary elements as long as the leaf
nodes are text
. This means we can construct arbitrary JSON
trees of to represent our content and let
Slate handle the rest.
import React, { useMemo } from "react";import ReactDOM from "react-dom";// Import the Slate editor factory.import { createEditor } from "slate";// Import the Slate components and React plugin.import { Slate, Editable, withReact } from "slate-react";const defaultValue = [{type: "stuff",children: [{text: "A line of text in a paragraph.",marks: []}]}];const App = () => {const editor = useMemo(() => withReact(createEditor()),[]);return (// Add the default value as a prop to the editor context.<Slate editor={editor} defaultValue={defaultValue}><Editable /></Slate>);};const rootElement = document.getElementById("root");ReactDOM.render(<App />, rootElement);
This works because the default React element used to render
a Slate element is a div
. Note the contents of the
data-slate-node
attributes in the resulting HTML.
<divdata-gramm="false"role="textbox"data-slate-editor="true"data-slate-node="value"contenteditable="true"style="outline: none; white-space: pre-wrap; overflow-wrap: break-word;"><divdata-slate-node="element"style="position: relative;"><span data-slate-node="text"><span data-slate-leaf="true"><span data-slate-string="true">A line of text in a paragraph.</span></span></span></div></div>
The type
field isn't special, we could name it whatever we
wanted. The children
field is important and needs to
exist. This is part of the
Node
interfaces.
the tldr; is that your elements need to have children
and
your Text
nodes need to have text
and marks
.
Everything else is up to you from how to structure your tree
to anything else relating to the keys that make up that
tree.
interface Element {children: Node[]}interface Text {text: stringmarks: Mark[]}