A beautiful, lightweight, framework-agnostic rich text editor built on Lexical. 100% MIT.
Seditor is a Notion-like, easy-to-configure rich text editor. The core is framework-agnostic (TypeScript, no React), with first-class React, Vue and Svelte bindings and a default theme.
- Beautiful by default โ Notion-like typography and a clean toolbar out of the box.
- Lightweight โ core is ~35 KB gzip (excluding the Lexical peer).
- Framework-agnostic core โ
seditor-corehas zero React dependency. Build your own bindings. - Config as data โ toolbar and config are plain objects, not JSX children.
- CSS variables theming โ dark mode is a variable override, not a class swap.
- 100% MIT โ no premium-gated features.
npm install seditor-react seditor-themePeer dependencies: react, react-dom, lexical.
import { Editor, Toolbar } from "seditor-react";
import "seditor-theme/index.css";
import "seditor-theme/dark.css";
export function App() {
return (
<Editor config={{ html: "<p>Hello <b>Seditor</b></p>" }}>
<Toolbar />
</Editor>
);
}Note: The
configprop is read once on mount. To update content after mount, use the imperative API (instance.setHTML(),instance.commands.*) via theonReadycallback.
import { createSeditor } from "seditor-core";
const instance = createSeditor({
namespace: "my-editor",
html: "<p>Initial content</p>",
editable: true,
});
instance.commands.toggleBold();
instance.commands.toggleHeading("h1");
instance.commands.toggleBulletList();
instance.commands.setLink("https://example.com");
instance.commands.undo();
instance.getHTML(); // -> "<p>...</p>"
instance.getJSON(); // -> Lexical EditorState JSON| Command | Description |
|---|---|
toggleBold / toggleItalic / toggleUnderline / toggleStrikethrough |
Inline text formats |
toggleHeading(tag) |
tag: "h1" | "h2" | "h3" |
setParagraph |
Convert block to paragraph |
toggleBulletList / toggleNumberedList |
Lists |
setLink(url) / unsetLink |
Links |
setAlign(align) |
align: "left" | "center" | "right" โ applies to text blocks and images |
setTextColor(color) / clearTextColor() |
Text color |
setTextBackgroundColor(color) / clearTextBackgroundColor() |
Text background color |
setFontSize(size) / clearFontSize() |
Font size (e.g. "16px") |
undo / redo |
History |
focus |
Focus the editor |
import type { SeditorPlugin } from "seditor-core";
const myPlugin: SeditorPlugin = {
name: "my-plugin",
nodes: [MyNode],
listeners: (editor) => [editor.registerUpdateListener(() => {})],
onInit: (editor) => {},
};Install the table plugin alongside a binding and the default theme:
npm install seditor-plugin-tableimport { Editor, Toolbar } from "seditor-react";
import { createTablePlugin } from "seditor-plugin-table";
import "seditor-theme/index.css";
<Editor
config={{
plugins: [
createTablePlugin({
defaultRowStriping: true,
scrollable: true,
}),
],
}}
>
<Toolbar />
</Editor>;The plugin supports cell selection, row and column operations, merge/unmerge, cell formatting, row/column/cell resizing, scrollable tables, frozen first row and column configuration, and independent outer/row/column border settings. See the Table Plugin guide for the complete API and framework notes.
The default theme uses CSS variables under the --se-* namespace. Override
them to rebrand:
:root {
--se-color-primary: #ff6b35;
--se-color-link: #ff6b35;
--se-font-size: 16px;
}Dark mode ships as seditor-theme/dark.css and activates via
prefers-color-scheme or the data-se-theme="dark" attribute.
pnpm install
pnpm dev # start playground
pnpm test # run all tests
pnpm typecheck
pnpm lint
pnpm build
pnpm size # check bundle size limitspackages/coremust not importreactor@lexical/react. CI checks this.- Config and toolbar are plain objects โ no JSX children.
- All theming uses CSS variables (
--se-*).
MIT ยฉ Seditor contributors