Skip to content
Edytor
Esc
↑↓navigate↵open⌘Jpreview
On this page

Rich text

The rich text plugin's block kinds, marks, Notion hotkeys, placeholders and formatting helpers, including link and color sanitization.

richTextPlugin defines the everyday document: paragraphs, headings, lists, to-dos, toggles, callouts, quotes, dividers, and ten marks, with Notion’s shortcuts. The core defines no block kinds, so <Edytor> includes it by default, after your plugins:

<script lang="ts">
	import { Edytor } from 'edytor';
</script>

<!-- Rich text, with no plugins listed. -->
<Edytor />

It takes no options. List it yourself only to place it elsewhere in the order, or with defaultPlugins={false} (see Default plugins). To change one of its kinds, define a kind with the same name in any of your plugins, since they come before it, or override only its snippet (see Blocks).

Block kinds

Kind Element Data Presets (markdown)
paragraph div with a p inside none Text
heading h1 to h3 from level { level: 'h1' } Heading 1 (# ), Heading 2 (## ), Heading 3 (### )
bulleted-list-item li none Bulleted list (- , * , + )
numbered-list-item li none Numbered list (1. , a. , i. )
todo-item div with a checkbox { checked: boolean } To-do list ([ ] , [] )
toggle details, content in summary none Toggle list (> )
callout div with an icon { icon: string } Callout (icon 💡)
quote blockquote none Quote (" )
divider hr, void, no content none Divider (---)

The kinds are listed in catalogue order, which the menus follow. Presets feed the slash menu (all under Basic blocks), markdown shortcuts and block menus. Their command ids are block.paragraph, block.heading1 to block.heading3, block.bulleted-list-item, block.numbered-list-item, block.todo-item, block.toggle, block.callout, block.quote and block.divider.

The plugin also defines kinds without presets, for content that arrives by paste or from stored documents:

  • ordered-list and unordered-list: ol and ul containers that render only their children. A new child takes the list-item kind.
  • list-item: an li.
  • details: the same as toggle.
  • horizontalRule: the same as divider.

Behavior worth knowing:

  • Bulleted and numbered items, to-dos and toggles are continuing kinds: Enter opens another of the same kind (an unchecked to-do), and in an empty one ends the list. Toggles, callouts and quotes are containers: Enter at the end of one with children, or of an open toggle, opens a first child, and in the middle of the header moves the rest of the text into that first child. Backspace at the start of any kind but a paragraph turns it into a paragraph first. Enter and Backspace by role has the full rules.
  • Headings have Notion’s three levels. A heading’s element follows data.level, h1 to h3, and so does its copied HTML. Without a level it is h1; any other level (a stored h5, say) renders and copies as h3, as pasting h4 to h6 gives a level h3 heading.
  • A toggle is a native <details>. The browser owns its open attribute, so opening and closing it is not an edit and is never reverted. Arrow keys skip the body of a closed toggle.
  • The todo-item checkbox (data-edytor-todo-checkbox) shows data.checked. Clicking it, or Mod + Enter in the to-do, flips checked as one undo step. It does not change in a readonly editor.
  • A callout shows data.icon in a data-edytor-callout-icon span, 💡 when it has none.
  • The paragraph element carries no classes of its own: style it through [data-edytor-type='paragraph'].
  • Pasted ol > li becomes a numbered item and other li a bulleted item.

Marks

Mark Element Value Toolbar button
bold strong true B
italic em true I
underline u true U
strike s true S
code code true </>
link a with href and target { href, target? } Link panel
superscript sup true
subscript sub true
color span with color a CSS color A ⌄ (text)
highlight span with background-color a CSS color A ⌄ (background)

Marks nest in this order, bold innermost. Typing at the end of a link extends it only when the caret is inside the link; typing at the edge of any other mark extends it.

Values are sanitized when rendered and copied. A link href keeps only http:, https:, mailto:, tel: and scheme-less URLs; any other scheme, such as javascript:, drops the href and leaves the text. A color that could inject CSS (it contains ;, braces, quotes, url( and similar) drops the style.

Pasted HTML maps b, i, u, strike and del to their marks, reads Google Docs’ styled spans for bold, italic, underline, strike, color and highlight, and turns mark into a yellow highlight.

Hotkeys

Keys Action
Mod + B Toggle bold
Mod + I Toggle italic
Mod + U Toggle underline
Mod + E Toggle inline code
Mod + Shift + S, Mod + Shift + X Toggle strikethrough
Mod + Shift + H Toggle red text color
Mod + Enter Check or uncheck the to-do (in a to-do only)

With a collapsed caret, a mark hotkey sets the mark for the next characters you type instead.

Notion’s “turn into” chords convert the block holding the caret, or, as in Notion, every block of a block selection or of a text range across blocks, as one undo step that keeps the selection (Code converts only the first):

Keys Turns into
Mod + Alt + 0 Text
Mod + Alt + 1 Heading 1
Mod + Alt + 2 Heading 2
Mod + Alt + 3 Heading 3
Mod + Alt + 4 To-do list
Mod + Alt + 5 Bulleted list
Mod + Alt + 6 Numbered list
Mod + Alt + 7 Toggle list
Mod + Alt + 8 Code (with the code plugin); a block with text or children keeps them, and the code block is inserted after it

Each runs the kind’s command (block.paragraph, block.heading1, …, block.code) and is claimed only when that command is registered.

The plugin also handles the formatting input types browsers send from their own menus and from iOS: bold, italic, underline, strikethrough, superscript, subscript, remove formatting, text and background color, insert link, ordered and unordered list (converts the block), and horizontal rule (inserts a divider at the caret, splitting the block when the caret is inside it).

Formatting from code

richTextOperations(edytor) returns the helpers the hotkeys and the toolbar use. They act on the current selection:

import { richTextOperations } from 'edytor';

const format = richTextOperations(edytor);

format.setMarkAtRange('bold'); // toggle, or stage at a caret
format.setMarkValueAtRange('highlight', '#fff3a3'); // set, never toggle
format.removeMarkAtRange('highlight'); // remove one mark
format.setLinkAtRange({ href: 'https://svelte.dev', target: '_blank' });
format.removeLinkAtRange();
format.removeAllMarksAtRange();
format.insertDividerAtSelection();

setLinkAtRange ignores an unsafe href. setMarkValueAtRange ignores an unsafe color. removeMarkAtRange does nothing at a collapsed caret.

Placeholders

richTextPlaceholder gives the kinds Notion’s placeholders (“Heading 1”, “List”, “To-do”, “Type ‘/’ for commands” in a focused paragraph…). Pass it to <Edytor placeholder={richTextPlaceholder} />. See Placeholder.

Was this page helpful?