SolidJS bindings for Tiptap
npm i solid-tiptap @tiptap/core @tiptap/pmyarn add solid-tiptap @tiptap/core @tiptap/pmpnpm add solid-tiptap @tiptap/core @tiptap/pmYou also need the Tiptap extensions you plan to use, such as @tiptap/starter-kit.
solid-js1.7 or later.@tiptap/coreand@tiptap/pm3.x.
Use solid-tiptap@0.7 for Tiptap 2.
import StarterKit from '@tiptap/starter-kit';
import { createTiptapEditor } from 'solid-tiptap';
function App() {
let ref!: HTMLDivElement;
const editor = createTiptapEditor(() => ({
element: ref,
extensions: [StarterKit],
content: `<p>Example Text</p>`,
}));
return <div id="editor" ref={ref} />;
}createTiptapEditor returns an accessor for the editor.
- The editor is created in an effect, after the element is mounted. The accessor returns
undefineduntil then. - The options function is tracked. When a signal it reads changes, the old editor is destroyed and a new one is created.
- The editor is destroyed when the owning component unmounts.
Because the options are tracked, read signals that should not recreate the editor with untrack.
import { untrack } from 'solid-js';
const editor = createTiptapEditor(() => ({
element: ref,
extensions: [StarterKit],
// Changing `initialContent` later does not recreate the editor.
content: untrack(initialContent),
}));Use the editor's own commands to update it after it is created.
editor()?.commands.setContent('<p>New content</p>');
editor()?.setEditable(false);Render the components that need the editor inside <Show>, so they only run once the editor exists.
const editor = createTiptapEditor(() => ({ ... }));
<Show when={editor()} keyed>
{(instance) => <Toolbar editor={instance} />}
</Show>keyed passes the editor itself to the children. Without it, Solid passes an accessor, so you would write editor={instance()}.
Tiptap's BubbleMenu and FloatingMenu extensions take an element option. Pass a mounted element the same way you pass the editor element.
import BubbleMenu from '@tiptap/extension-bubble-menu';
function App() {
let container!: HTMLDivElement;
let menu!: HTMLDivElement;
const editor = createTiptapEditor(() => ({
element: container,
extensions: [StarterKit, BubbleMenu.configure({ element: menu })],
}));
return (
<>
<div ref={menu}>
<Show when={editor()} keyed>
{(instance) => <Toolbar editor={instance} />}
</Show>
</div>
<div ref={container} />
</>
);
}Tiptap does not notify Solid when the editor changes. createEditorTransaction reads a value again after every editor transaction, so you can use it in JSX and effects.
import { createEditorTransaction } from 'solid-tiptap';
const isBold = createEditorTransaction(
() => props.editor,
(editor) => editor.isActive('bold'),
);
createEffect(() => {
if (isBold()) {
// ...
}
});The read function receives whatever the first accessor returns. If that can be undefined, handle it in the read function.
const length = createEditorTransaction(
editor,
(instance) => instance?.state.doc.textContent.length ?? 0,
);Each helper wraps createEditorTransaction for one part of the Editor API. Each takes an editor accessor, which may return undefined. Each returns an accessor that returns undefined while there is no editor.
| Helper | Reads | Returns |
|---|---|---|
useEditorHTML |
editor.getHTML() |
string |
useEditorJSON |
editor.getJSON() |
JSONContent |
useEditorIsActive |
editor.isActive(...) |
boolean |
useEditorIsEmpty |
editor.isEmpty |
boolean |
useEditorIsEditable |
editor.isEditable |
boolean |
useEditorIsFocused |
editor.isFocused |
boolean |
const html = useEditorHTML(editor);
createEffect(() => {
saveHTML(html());
});const json = useEditorJSON(editor);
createEffect(() => {
const data = json();
if (data) {
uploadJSON(data);
}
});useEditorIsActive takes a name accessor with optional attributes, a name accessor alone, or attributes alone.
// A node or mark by name.
const isBold = useEditorIsActive(editor, () => 'bold');
// A node or mark by name and attributes.
const isHeading = useEditorIsActive(editor, () => 'heading', { level: 1 });
// Any node or mark with these attributes.
const isCentered = useEditorIsActive(editor, { textAlign: 'center' });Creates a Tiptap editor. Also exported as createEditor, useEditor and useTiptapEditor.
optionsis a function that returns Tiptap's editor options.elementis required.- Returns
() => Editor | undefined.
editoris a function that returns anEditororundefined.readis called with the result ofeditor(). Its result is returned by the accessor.- Returns an accessor that runs
readagain after every editor transaction.
UseEditorOptions<T extends HTMLElement>is the options type forcreateTiptapEditor.BaseEditorOptionsis Tiptap'sEditorOptionswithoutelement, with every field optional.
MIT © lxsmnsyc
