---
title: Markdown shortcuts
description: The markdown shortcuts plugin converts a block when you type a prefix such as "# " or "- " at its start, and formats text typed as **bold**, *italic*, `code` or ~strike~, as in Notion.
icon: hash
---

`markdownShortcutsPlugin` brings Notion's markdown as you type. A prefix at the start of a block converts it: `# ` makes a heading, `- ` a bulleted list item, ` ``` ` a code block. The prefixes come from the `markdown` field of each block kind's presets, so the plugin has no list of its own. Inline, `**bold**`, `*italic*` and the others apply their mark when you type the closing marker.

```svelte title="Editor.svelte" check
<script lang="ts">
	import { Edytor, markdownShortcutsPlugin, codePlugin } from 'edytor';
</script>

<Edytor plugins={[codePlugin, markdownShortcutsPlugin]} />
```

It takes no options.

## Prefixes

With the rich text and code plugins, the prefixes are Notion's:

| Type                        | Converts to                        |
| --------------------------- | ---------------------------------- |
| `# `                        | Heading 1                          |
| `## `                       | Heading 2                          |
| `### `                      | Heading 3                          |
| `- `, `* ` or `+ `          | Bulleted list item                 |
| `1. `, `a. ` or `i. `       | Numbered list item                 |
| `[] ` or `[ ] `             | To-do                              |
| `" `                        | Quote                              |
| `> `                        | Toggle                             |
| `---`                       | Divider                            |
| ` ``` `                     | Code block (code plugin)           |

:::note
As in Notion, `> ` makes a toggle and `" ` a quote.
:::

## How it triggers

A conversion happens when the character you type completes a prefix, and all of these hold:

- the caret is collapsed in the block's first text;
- the text from the block's start to the caret plus the typed character equals the prefix exactly (so `# ` works at the start of a block, not after other words);
- the block is convertible (not a void or island block, and not inside one); in a list's item, the new kind takes the item out of the list ([Containers](/docs/concepts/blocks#containers));
- the typed input is one character.

Removing the prefix and converting the block are one operation (`setBlock`, with a `deleteContentAtRange` step for the prefix): a plugin that refuses either refuses both. When the conversion is refused, the character is inserted as normal text instead. The conversion is its own undo step, separate from the typing before it: undo after `## ` leaves a paragraph `##`. After the conversion the caret is at the start of the converted block, or of its first child when the kind starts with one (a code block's first line).

As in Notion, text after the caret is kept: typing `- ` at the start of `hello` makes a bulleted item `hello`, with the caret before it.

A kind with an `empty` shape, such as the divider, replaces the block's content and children, so its prefix converts only a block that holds nothing but the prefix: `---` typed before `hello` stays text. Other kinds keep them.

A kind that renders no content, such as the divider, cannot hold the caret: the conversion also adds a fresh block of the default kind where the divider lands after it, in the same step, and the caret lands there. In a list's empty item, both go out of the list, which splits around them (`---` in an item gives a divider and a paragraph between the list's two halves). Undo removes both.

## Inline markdown

Typing the closing marker after marked-up text applies the mark and removes both markers:

| Type                         | Mark          |
| ---------------------------- | ------------- |
| `**bold**`                   | `bold`        |
| `*italic*` or `_italic_`     | `italic`      |
| `` `code` ``                 | `code`        |
| `~strike~` or `~~strike~~`   | `strike`      |

- The text between the markers must be non-empty and not start or end with a space: `* a*` and `** **` stay text.
- Removing the markers and applying the mark are one undo step, separate from the typing before it; undo brings the markers back as text (`say **b**` undoes to `say **b*`: the closing character was never inserted). The step runs as an `inlineMarkdown` operation, visible to `onBeforeOperation`. A plugin that refuses it leaves the closing character typed as text.
- Typing continues without the mark.
- It works anywhere in a text, at a collapsed caret, except in code lines, and only for marks the editor defines.

## Adding a prefix

Declare `markdown` on a preset of your block kind:

```ts
blocks: {
	note: {
		snippet: note,
		presets: [{ label: 'Note', icon: 'ℹ', data: { tone: 'info' }, markdown: ['!! '] }]
	}
}
```

The last character of a prefix triggers it. A prefix without a trailing space, such as `---`, converts as soon as it is complete. See [Blocks](/docs/customization/blocks#presets) for presets and the [example plugin](/docs/plugins/example-plugin) for a full kind.
