Slate is split into small packages. For a React editor, install the core editor, the DOM helpers, the React renderer, and React itself.
pnpm add @platejs/slate @platejs/slate-dom @platejs/slate-react react react-dompnpm add @platejs/slate @platejs/slate-dom @platejs/slate-react react react-domOnce the packages are installed, import the React editor pieces from
@platejs/slate-react.
import { Editable, Slate, type SlateChange, useSlateEditor } from '@platejs/slate-react'import { Editable, Slate, type SlateChange, useSlateEditor } from '@platejs/slate-react'Before we render anything, let's define the document shape for this editor.
type CustomText = { text: string }
type ParagraphElement = { type: 'paragraph'; children: CustomText[] }
type CustomValue = ParagraphElement[]
const initialValue: CustomValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]type CustomText = { text: string }
type ParagraphElement = { type: 'paragraph'; children: CustomText[] }
type CustomValue = ParagraphElement[]
const initialValue: CustomValue = [
{
type: 'paragraph',
children: [{ text: 'A line of text in a paragraph.' }],
},
]CustomValue is the TypeScript shape of this editor's primary document.
Passing it to useSlateEditor keeps element and text types attached to the
editor API.
Call useSlateEditor inside the component so React keeps the same editor
object for the component lifetime.
Now we can render the editor with <Slate> and <Editable>.
const App = () => {
const editor = useSlateEditor<CustomValue>({ initialValue })
return (
<Slate editor={editor}>
<Editable />
</Slate>
)
}const App = () => {
const editor = useSlateEditor<CustomValue>({ initialValue })
return (
<Slate editor={editor}>
<Editable />
</Slate>
)
}The editor is seeded by useSlateEditor({ initialValue }). <Slate>
provides that existing editor to everything underneath it, and <Editable>
renders the editable document surface.
This is the smallest useful Slate editor. If you render App, you should see a paragraph with the text A line of text in a paragraph. When you type, Slate updates the document through the editor runtime.
Listening for changes
Most applications save the document value somewhere. Pass onChange to <Slate> and save when change.valueChanged is true.
const App = () => {
const editor = useSlateEditor<CustomValue>({ initialValue })
const handleChange = (
nextValue: CustomValue,
change: SlateChange<CustomValue>
) => {
if (!change.valueChanged) return
localStorage.setItem('content', JSON.stringify(nextValue))
}
return (
<Slate
editor={editor}
onChange={handleChange}
>
<Editable />
</Slate>
)
}const App = () => {
const editor = useSlateEditor<CustomValue>({ initialValue })
const handleChange = (
nextValue: CustomValue,
change: SlateChange<CustomValue>
) => {
if (!change.valueChanged) return
localStorage.setItem('content', JSON.stringify(nextValue))
}
return (
<Slate
editor={editor}
onChange={handleChange}
>
<Editable />
</Slate>
)
}Use initialValue as the one-shot initial document passed to useSlateEditor.
If your app needs to replace the whole document after the
editor exists, use an explicit editor update instead of treating <Slate> like
a controlled <textarea>.
Next steps
The editor is rendering plain text. Next, add event handlers so the editor can respond to keyboard shortcuts.