---
title: Code
description: The code plugin adds code blocks made of editable lines, highlighted with TanStack Highlight, with tab, auto-pairing and copy.
icon: code
---

`codePlugin` adds code blocks. A code block is an island that holds one `codeLine` child per line of code; each line is highlighted as JSX with TanStack Highlight (`@tanstack/highlight`, a dependency of `edytor`).

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

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

It takes no options. Importing it also imports its stylesheet, which gives code blocks Notion's look.

## What it adds

| Definition | Kind  | Details                                                                                          |
| ---------- | ----- | ------------------------------------------------------------------------------------------------ |
| `code`     | block | An island that renders only its children, under a header with the language and a Copy button.   |
| `codeLine` | block | One line of code. The default child of `code`.                                                   |
| `codeToken`| mark  | A highlighting token, rendered as `<span class="th-…">`. Computed on render, never stored.       |

A code block has one preset, "Code", with the command id `block.code`, the markdown shortcut ` ``` ` (three backticks typed at the start of an empty block) and the slash menu section `Media`. <kbd>Mod</kbd> + <kbd>Alt</kbd> + <kbd>8</kbd> converts to it when the rich text plugin is loaded, as it is by default. An empty block converts in place, into a code block of one empty line. A block with text or children stays as it is: the command (the slash menu, the chord) inserts an empty code block after it instead, and the caret moves to its line. See [Presets](/docs/customization/blocks#presets).

A stored code block looks like this:

```json
{
	"type": "code",
	"children": [
		{ "type": "codeLine", "content": [{ "text": "const idea = \"start somewhere\";" }] },
		{ "type": "codeLine", "content": [{ "text": "console.log(idea);" }] }
	]
}
```

## Highlighting

Each `codeLine` declares a `transformText` that tokenizes its text with a highlighter built from `createHighlighter({ languages: [jsx] })`. Tokens become `codeToken` marks carrying the token's class name. For `const idea = "x";` the tokenizer answers a `keyword` token, plain text, a `string` token and plain text, rendered as:

```html
<span data-edytor-mark="codeToken"><span class="th-keyword">const</span></span> idea = <span data-edytor-mark="codeToken"><span class="th-string">"x"</span></span>;
```

The tokens are decorations: they exist only in the rendered DOM, never in the document, so collaborators and the clipboard see plain text. The language is always JSX (which also covers JavaScript), and the header label reads "JavaScript".

The bundled stylesheet colors the `th-*` classes with Notion's light syntax theme:

| Classes                                      | Color     |
| -------------------------------------------- | --------- |
| `th-comment`, `th-meta`                      | `#708090` |
| `th-keyword`, `th-link`                      | `#0077aa` |
| `th-string`, `th-attr`, `th-selector`, `th-inserted` | `#669900` |
| `th-function`, `th-type`                     | `#dd4a68` |
| `th-number`, `th-literal`, `th-tag`, `th-property`, `th-deleted` | `#990055` |
| `th-variable`                                | `#ee9900` |
| `th-operator`                                | `#9a6e3a` |

Override those classes to theme it.

## Editing

Inside a code line:

| Keys                                   | Action                                                                             |
| -------------------------------------- | ---------------------------------------------------------------------------------- |
| <kbd>Enter</kbd>, <kbd>Shift</kbd> + <kbd>Enter</kbd> | Start a new line                                                      |
| <kbd>Tab</kbd>                         | At a caret, insert a tab character, or accept an inline suggestion. Over a selection, add a tab at the start of every line it touches, as one undo step; the selection keeps its characters |
| <kbd>Shift</kbd> + <kbd>Tab</kbd>      | Remove one leading tab, or up to two spaces, from the caret's line or every line the selection touches |
| <kbd>Mod</kbd> + <kbd>A</kbd>            | From a non-empty line, select the text of the whole code block                     |
| <kbd>Escape</kbd>                      | Dismiss an inline suggestion                                                       |

- Typing `(`, `[`, `{`, `"` or `'` at a caret also inserts the closing character. A quote pairs only at a word boundary: the apostrophe in `don't` stays single.
- Typing a closing character (`)`, `]`, `}`, `"`, `'`) right before the same character steps over it instead of inserting another: `f(a)` types as `f(a)`, not `f(a))`.
- A line break inside a line splits it: each code line holds exactly one line of text.
- <kbd>Backspace</kbd> at the start of the first line and <kbd>Delete</kbd> at the end of the last line do nothing: a line never merges out of its block.
- <kbd>Delete</kbd> at the end of the block right before a code block removes that block if it is empty, and does nothing otherwise. The caret goes to the end of the text before the removed block, or to the start of the code when nothing comes before it.
- <kbd>Backspace</kbd> in an empty block right after a code block removes that block and puts the caret at the end of the last line. In a block with text it moves the caret to the end of the code and merges nothing.
- A selection that ends at the start of a line leaves that line out of <kbd>Tab</kbd> and <kbd>Shift</kbd> + <kbd>Tab</kbd>, as code editors do.
- The Copy button, shown while the block is hovered, writes the block's text, lines joined with newlines, to the clipboard, and reads "Copied" for a moment.

A code block is an island: merges never cross its edge, and a block selection walked with the arrow keys does not enter it from outside. It is also not convertible, so the slash menu and markdown shortcuts do not apply inside it.

## Styling

The stylesheet draws the block like Notion's: a `#f7f6f3` panel rounded 10px, padded `34px 16px 32px 32px`, with the language at the top left and the Copy button at the top right. The code is set in an `SFMono-Regular`, Menlo, Consolas stack at 85%, with a tab size of 2. No utility classes are involved; everything goes through attributes and classes you can override:

| Selector                        | Element                                        |
| ------------------------------- | ---------------------------------------------- |
| `[data-edytor-type='code']`     | The block panel                                |
| `[data-edytor-code-header]`     | The header, void chrome the caret never enters |
| `[data-edytor-code-language]`   | The language label                             |
| `[data-edytor-code-header] button` | The Copy button                             |
| `pre.th-code`                   | The code area                                  |

```css
[data-edytor-type='code'] {
	background: #1f1f1f;
	color: #e6e6e6;
}
[data-edytor-type='code'] pre.th-code {
	font-size: 14px;
}
```

The [Notion theme](/docs/customization/styling#notion-theme) reads the panel color from `--notion-code-panel`.
