Zero dependencies · Real-time collab · MIT

What you write
is what you get.

Rune is a headless, config-driven WYSIWYG editor built from scratch — no ProseMirror, no Slate, no bundler. Wire up every block, mark, and plugin from a single file, add real-time collaboration when you need it, then style 100% of it with CSS variables.

$npm install @parityfox/rune-editor
Vanilla JS, React, Vue, Svelte & Web ComponentNo build step requiredSanitized, safe HTML out
editor.html — live
Type / for commands+B bold#+space headingOutput is sanitized — javascript: URLs blocked
Batteries included

Everything a document needs, nothing it doesn’t.

Fourteen block types, a dozen inline marks, and seven plugins — plus alignment, indent and line-height. Each one is a toggle in your config; turn things off and the toolbar, menus, and shortcuts all follow. Export to clean HTML, plain text, Markdown, or print.

Block types

14 blocks

From headings and lists to tables, callouts, code, task lists, toggles, columns, and embedded video — every block is insertable from the slash menu.

HeadingBullet listOrderedQuoteCode blockCalloutTask listTableImageVideoToggleColumnsDivider

Marks & formatting

12 marks + 5 controls

Bold, italic, links, inline code, super/subscript, plus font size, family, text colour and highlight — with alignment, indent and line-height. All on sane keyboard shortcuts.

BoldItalicUnderlineStrikeCodeLinkSuper/subFont sizeColorHighlightAlignLine height

Plugins

7 plugins

Markdown shortcuts, inline markdown, smart typography, find & replace with regex, drag-to-reorder blocks, a format painter, and emoji autocomplete — drop in as plain objects.

Markdown shortcutsInline markdownSmart typographyFind & replaceDrag to reorderFormat painterEmoji
Real-time, opt-in

Write together, in real time.

A Yjs-based collaboration layer lets many people edit one document at once — over the network or in-process. The core editor stays dependency-free; the collab deps load only when you turn it on.

ABCD
Alice, Bea and 2 others are editing — live cursors, selections and typing indicators.
connected

Live presence

cursors · selections · typing

See every collaborator's caret, selection highlight and typing indicator move in real time, each in their own colour.

Comments

anchored threads

Anchor threaded comments to any selection. They follow the text as the document changes and resolve when you're done.

Tracked suggestions

accept / reject changes

Suggestion mode records edits as proposed changes. Reviewers accept or reject them inline — no overwriting each other's work.

Offline-first

IndexedDB persistence

Keep editing offline; changes persist locally and sync automatically when the connection returns. Full block coverage — incl. per-cell tables.

collab.js
import * as Y from 'yjs';
import { WebSocketProvider } from '@parityfox/rune-editor/collab/provider.js';
import { bindParagraphSpike } from '@parityfox/rune-editor/collab/paragraph-binding.js';
import { bindPresence } from '@parityfox/rune-editor/collab/presence.js';

const doc = new Y.Doc();
const provider = new WebSocketProvider('ws://localhost:1234', 'my-doc', doc);

bindParagraphSpike(editor, doc);
bindPresence(editor, doc, provider.awareness, {
  name: 'Alice', color: '#2563eb',
});
One file, total control

Config-driven, to the core.

Flip a boolean in rune.config.js and the entire editor reshapes around it — no other file needs touching.

  • 01

    Toggle any capability

    Every block, mark, and plugin is a single key. Off means it’s gone from the bundle’s surface area, the toolbar, and the slash menu.

  • 02

    Menus stay in sync

    Toolbar, bubble menu, slash menu, and keyboard shortcuts all read from the same config. Define order once.

  • 03

    Extend with a plain object

    Add custom blocks and marks by handing Rune an object. No class hierarchy, no schema DSL to learn.

rune.config.js
const config = {

  blocks: {
    heading:    true,  // H1–H3
    bulletList: true,
    codeBlock:  true,
    callout:    true,
    taskList:   true,
    table:      true,
  },

  marks: {
    bold: true, italic: true,
    link: true, code: true,
    textColor: true,
  },

  plugins: {
    markdownShortcuts: true,
    findReplace:       true,
    dragReorder:       true,
  },
};

export default config;
Headless & theme-able

Style every pixel with CSS variables.

Rune ships no opinions about how it looks. Colours, radii, and type are all custom properties on :root. Flip data-theme="dark" for instant dark mode — try the toggle up top.

Accent
--rune-color-accent
Background
--rune-color-bg
Foreground
--rune-color-fg
Surface
--rune-color-surface
Border
--rune-color-border
Muted
--rune-color-muted
runeProseMirrorSlateQuill
Zero dependenciesYesNoNoNo
No build stepYesNoNoYes
Headless / CSS-var themingYesPartialPartialNo
Config-driven toolbarYesNoNoPartial
Works without a frameworkYesYesNoYes
Real-time collaboration built inYesAdd-onNoNo
Five-minute setup

Drop it into anything.

Vanilla JS, React, Vue, Svelte, or a Web Component — the same engine, five adapters. Pick your stack and copy the snippet.

quick-start
import { createFromConfig } from '@parityfox/rune-editor';
import config from './rune.config.js';
import '@parityfox/rune-editor/styles';

const editor = createFromConfig('#app', config, {
  content: '<p>Start writing…</p>',
  onChange(html) { console.log(html); },
});

Build an editor that
feels like yours.

Zero dependencies. One config file. Full control of every block, mark, and pixel.

Get started