Plite ships its editor runtime and optional subpaths in one package. Install it with React and React DOM for a React editor.
Install the React editor path first.
| Package | Owns |
|---|---|
plitejs | editor runtime, document model, transactions, helper APIs |
plitejs/dom | DOM conversion, clipboard helpers, browser environment helpers |
plitejs/react | <EditorRoot>, <Editable>, React hooks, editor setup |
react, react-dom | React peer packages |
Import the React editor pieces from plitejs/react.
import {
Editable,
EditorRoot,
type ValueChangeContext,
useEditor,
} from "plitejs/react";import {
Editable,
EditorRoot,
type ValueChangeContext,
useEditor,
} from "plitejs/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 useEditor keeps element and text types attached to the
editor API.
Call useEditor inside the component so React keeps the same editor
object for the component lifetime.
Render the editor with <EditorRoot> and <Editable>.
const App = () => {
const editor = useEditor<CustomValue>({ initialValue });
return (
<EditorRoot editor={editor}>
<Editable />
</EditorRoot>
);
};const App = () => {
const editor = useEditor<CustomValue>({ initialValue });
return (
<EditorRoot editor={editor}>
<Editable />
</EditorRoot>
);
};useEditor({ initialValue }) seeds the editor. <EditorRoot>
provides that existing editor to everything underneath it, and <Editable>
renders the editable document surface.
This is the smallest useful Plite editor. If you render App, you should see a paragraph with the text A line of text in a paragraph. When you type, Plite updates the document through the editor runtime.
Most applications save the document value somewhere. Use onValueChange for
a single-root document.
const App = () => {
const editor = useEditor<CustomValue>({ initialValue });
const handleValueChange = ({
value,
}: ValueChangeContext<CustomValue>) => {
localStorage.setItem("content", JSON.stringify(value));
};
return (
<EditorRoot editor={editor} onValueChange={handleValueChange}>
<Editable />
</EditorRoot>
);
};const App = () => {
const editor = useEditor<CustomValue>({ initialValue });
const handleValueChange = ({
value,
}: ValueChangeContext<CustomValue>) => {
localStorage.setItem("content", JSON.stringify(value));
};
return (
<EditorRoot editor={editor} onValueChange={handleValueChange}>
<Editable />
</EditorRoot>
);
};Use initialValue as the one-shot initial document passed to useEditor.
If your app needs to replace the whole document after the
editor exists, use an explicit editor update instead of treating <EditorRoot> like
a controlled <textarea>.
The editor is rendering plain text. Next, add event handlers so the editor can respond to keyboard shortcuts.