Skip to content

Editor

InkwellEditor is a WYSIWYG Markdown editor. The content you see is visually formatted, but the underlying data is always a plain Markdown string.

The editor is a controlled React component. You own the state.

import { InkwellEditor } from "@railway/inkwell";
import { useState } from "react";
function App() {
const [content, setContent] = useState("# Hello **world**");
return <InkwellEditor content={content} onChange={setContent} />;
}

content accepts any Markdown string. onChange fires on every edit with the updated Markdown.

Type Markdown syntax and the editor renders it visually as you go. Type ## at the start of a line and it becomes a heading. Wrap text in ** and it turns bold. The Markdown characters stay in the document — they’re styled to show you what the output will look like.

All formatting is enabled by default.

Type these at the beginning of a line:

PatternResult
# through ###### Headings (h1–h6)
- , * , or + List items
> Blockquotes
```Fenced code blocks

Wrap text with these markers anywhere in a line:

SyntaxResult
**text**Bold
_text_Italic
~~text~~Strikethrough
`text`Inline code
ShortcutAction
⌘B / Ctrl+BToggle bold
⌘I / Ctrl+IToggle italic
⌘D / Ctrl+DToggle strikethrough

These shortcuts come from the built-in bubble menu plugin. You can customize or disable them.

Use the decorations prop to turn off specific block-level formatting. All decorations are enabled by default — you only need this prop to disable something.

<InkwellEditor
content={content}
onChange={setContent}
decorations={{ codeBlocks: false, heading4: false, heading5: false, heading6: false }}
/>
DecorationDefaultControls
heading1heading6trueIndividual heading levels
liststrueUnordered list items (-, *, +)
blockquotestrueBlockquotes (>)
codeBlockstrueFenced code blocks (```)

When a decoration is disabled, its syntax is treated as plain text with no visual formatting.

Fenced code blocks are highlighted with highlight.js by default. Import a highlight.js CSS theme for colors to appear:

import "highlight.js/styles/github-dark.css";

To use a different highlighter, pass it through the rehypePlugins prop:

import { InkwellEditor } from "@railway/inkwell";
import rehypeShiki from "@shikijs/rehype";
<InkwellEditor
content={content}
onChange={setContent}
rehypePlugins={[[rehypeShiki, { theme: "github-dark" }]]}
/>

The same rehypePlugins prop is available on InkwellRenderer.

The editor includes a floating formatting toolbar by default. Select text and the toolbar appears with bold, italic, and strikethrough buttons.

To disable it:

<InkwellEditor content={content} onChange={setContent} bubbleMenu={false} />

To customize the toolbar items, see the Plugins guide.

Type: string

The Markdown string to display and edit. When collaboration is enabled, this only seeds an empty document — the Yjs shared type becomes the source of truth after that.

Type: (markdown: string) => void

Called on every document change with the updated Markdown string.

Type: string

Text shown when the editor is empty.

Type: string

CSS class applied to the outer wrapper element.

Type: InkwellDecorations

Controls which block-level formatting the editor recognizes. All enabled by default. See Disabling formatting.

Type: InkwellPlugin[]

Additional plugins to load. See Plugins.

Type: boolean — default: true

Whether to include the built-in floating toolbar. Pass false to disable it entirely, or use a custom bubble menu via plugins instead.

Type: CollaborationConfig

Enable real-time collaborative editing. See Collaboration.

Type: RehypePluginConfig[]

Custom rehype plugins for code block highlighting. Accepts a plugin function or a [plugin, options] tuple. See Syntax highlighting.