For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt, and this page is available as Markdown at https://wallarm.github.io/design-system/components/code-editor.md.

CodeEditor

An editable code surface that is visually the same component as CodeSnippet and reuses its header, tabs, actions and show-more — reach for CodeSnippet whenever the code is only read, since a page that never renders CodeEditorContent never loads the editor engine. Colours come from the same CodeSnippetAdapterProvider (plain text without one), so a read-only editor matches the snippet except for a native thin scrollbar, a caret and selection drawn by the editor, and an HTTP JSON body that stays coloured when Content-Type has parameters or the body has blank lines. The adapter re-tokenizes the whole document after each pause in typing, which stays within a frame up to about 2 000 lines with Prism; longer documents still edit smoothly, their colours just settle a moment later. Like the snippet, the editor sits inside a display: contents wrapper that keeps it mounted across fullscreen, so space editors with their own className (for example mt-16) or a gap on the parent — not space-*, divide-* or child selectors on the parent. lines, folds, completions and schema are compared by identity, so memoise them or hoist them to module constants — a new value on every render reconfigures the editor. Autocomplete suggests language keywords and words already in the document for every language; HTTP methods and headers and JSON Schema values have their own contextual suggestions. Pass completions to add application-specific results. Syntax errors are best-effort: JSON reports the JSON.parse message, JavaScript and TypeScript use Babel, Lua uses luaparse (first error only), YAML and Python use their Lezer parsers, which still flag a few valid Python forms such as lambda a, /, b and parenthesised with items. lines and folds stay on their line numbers while typing; to keep a decoration on a piece of text, recompute it from value (for example useMemo(() => linesFor(value), [value]), or a folds function such as getHttpFolds). The editor injects <style> tags, so under a strict Content Security Policy pass cspNonce. Tab indents, so keyboard users leave with Escape, then Tab, or press Ctrl+M (Alt+Shift+M on macOS) to switch Tab between indenting and moving focus — point aria-describedby at text holding CODE_EDITOR_KEYBOARD_HINT to tell screen-reader users.

Source · Storybook

Usage

import { CodeEditorContent, CodeEditorRoot } from '@wallarm-org/design-system/CodeEditor';

Examples

With header

The CodeSnippet header works unchanged: a title and the copy, wrap and fullscreen buttons. Copy puts the edited value on the clipboard, and fullscreen keeps the undo history.

Floating actions

Without a header, CodeSnippetActions as a direct child floats over the top-right of the editor, as it does over a snippet.

Line numbers and starting line

lineNumbers on CodeEditorContent is the equivalent of CodeSnippetLineNumbers, and startingLineNumber renumbers the gutter for a fragment taken from a longer file.

Line colors and prefixes

lines uses the same LineConfig as CodeSnippet: seven semantic colours, text prefixes for a hand-made diff, and ReactNode prefixes such as a tooltipped icon. Lines are static and tied to line numbers, so they stay on their number while you type.

Ranges

ranges colours character offsets inside a line (start inclusive, end exclusive). The line keeps its background, and only the range gets the colour.

Http request with prism

language='http' under Prism. The colours come from the adapter and the structure (start line, headers, JSON body) comes from the editor's own HTTP grammar.

Http response with shiki

The response side under Shiki. After an edit, highlighting is repainted in the background, so for a moment you may see the previous colours mapped through your change.

Http folds

folds as a function of the value: getHttpFolds runs again after edits, so adding a header grows the headers fold. Collapsed state is kept by fold id. The second editor starts at line 100, and the fold function gets the same offset.

Show more

maxLines limits the height, and the editor scrolls inside it (no rows are removed). The button appears on its own when at least three lines are hidden. Render CodeSnippetShowMoreButton yourself only to pass props to the real button.

Wrap

Wrapping can be uncontrolled (defaultWrapLines, driven by the wrap button) or controlled (wrapLines plus onWrapLinesChange). Colour sticks and prefixes stay on the first visual row.

Sizes

The same three sizes as CodeSnippet: sm (12px, default), md (14px) and lg (16px), each on 20px rows.

Read only

readOnly lets you select, search and copy without focusing the code or showing a caret. Use it when the value can change elsewhere but not here. If it never changes, use CodeSnippet.

Tabs

Tabs swap value, and documentId swaps the editor's whole state with it, so each tab keeps its own undo history, selection and collapsed folds.

Parity default

Parity check: the same code and props in CodeSnippet and in a read-only CodeEditor should look identical. Accepted differences: a native thin scrollbar, and CodeMirror's caret and selection layer (shown only while focused).

Parity line colors

Parity check with line colours, text prefixes and a tooltipped icon prefix: same stick, same tint, same prefix column.

Parity folds collapsed

Parity check with the headers fold collapsed: same chevrons and the same summary row in place of the hidden lines.

Parity http prism

Parity check for an HTTP request with a JSON body, both under the same Prism provider: token colours come from the same adapter.

Parity ranges

Parity check for ranges: the same bold, coloured spans inside the same tinted lines.

Parity wrap

Parity check for wrapped lines: long lines break at the same points and line numbers stay on the first row of each wrapped line.

Parity sizes

Parity check for the three sizes: the same font size, line height and gutter padding per size.

Parity frame

Parity check for the shared frame: a header with tabs and actions, and floating actions without a header, sit in the same place with the same spacing.

Parity show more

Parity check for maxLines: the same clamped height and the same "Show N more lines" button.

Languages

Colours come from the adapter (Shiki here); JavaScript, TypeScript, Python and Lua parsers load on first use.

Syntax errors

Syntax errors for every language with a structure parser: JSON and an HTTP JSON body use the precise JSON.parse message, JavaScript and TypeScript use Babel, Lua uses luaparse (first error only), YAML and Python report Lezer error nodes. Hover an underline to read the message; the count below each editor comes from onDiagnosticsChange. Bash and plain text have no syntax diagnostics.

Diff

original turns on diff mode: added and changed lines read as success lines with a +, removed lines as danger rows with a - and no line number. The rows stay editable, and the diff updates as you type.

Diff wrap lines

With wrapping on, a removed line longer than the frame wraps inside its danger row and its - marker stays on the first visual row, beside where the line starts.

Analytics attributes

Where analytics attributes land. data-* / aria-* / id on CodeEditorContent reach the typing surface (role="textbox"), chrome buttons take them directly, and fold buttons take them through toggleProps / summaryProps. Popups drawn by the editor (autocomplete, tooltips, search panel) are not attributable — see CodeEditor/ANALYTICS_GAPS.md.

Editing workflow

A controlled HTTP request with header and body folds and a JSON Schema on the body — retries breaks the schema's maximum on purpose, so the underline and its hover message are part of the example. The editor's description points at CODE_EDITOR_KEYBOARD_HINT.

Tabs keep history

Each tab passes its own documentId, so switching swaps in that document's undo history, selection and collapsed folds instead of replacing the text of one shared document.

Long document

2 000 read-only lines clamped by maxLines — the top of the envelope the colour painter is built for, where readOnly still lets the text be selected, searched and copied.

Fullscreen inside drawer

Fullscreen opened from a modal Drawer stacks above it: the editor and its buttons take pointer and keyboard input, and Escape leaves fullscreen while the drawer stays open.

Props

PropTypeDefaultDescription
valuestring | undefinedControlled document
defaultValuestring | undefinedUncontrolled initial document (controlled vs uncontrolled is locked on first render)
onChange((value: string) => void) | undefinedFired for user edits only, never for external value syncs
languageCodeEditorLanguage | undefinedPassed verbatim to the syntax adapter; selects the structural parser
documentIdstring | undefinedDocument identity: changing it swaps to that document's cached state (undo history, selection, folds). Requires a controlled value: when the id changes, value must already hold that document's text (an uncached id starts from value). In uncontrolled mode the new document would be seeded with the previous document's text.
readOnlyboolean | undefinedNon-editable and unfocusable content; text remains selectable, searchable and copyable
startingLineNumbernumber | undefinedOffsets gutter numbers; lines, folds, diagnostics use absolute numbers
linesRecord<number, LineConfig> | undefinedPer-line configuration keyed by absolute line number (static, see spec D6). Compared by identity: memoise it (or hoist it to a constant) — a new object every render reconfigures the lines and gutters on every render.
foldsCodeEditorFolds | undefinedFold regions, or a function re-run after edits; collapsed state is kept by id. Compared by identity: memoise the array or pass a stable function (e.g. getHttpFolds) — an inline array/arrow reconfigures folds on every render and bypasses the re-run debounce.
wrapLinesboolean | undefinedControlled line wrapping
defaultWrapLinesboolean | undefinedUncontrolled initial line wrapping
onWrapLinesChange((wrap: boolean) => void) | undefinedFired when the wrap button toggles wrapping
maxLinesnumber | undefinedHeight clamp in rows; auto-renders CodeSnippetShowMoreButton unless one is a direct child
originalstring | undefinedEnables diff mode against this text
schemaJsonSchema | undefinedJSON Schema for the document (json) or the JSON body (http)
completionsCodeEditorCompletionSource[] | undefinedConsumer completion sources, added after the built-in ones. Compared by identity: a new array reconfigures autocomplete and closes an open list, so keep it stable (module constant or useMemo).
diagnosticsCodeEditorDiagnostic[] | undefinedExternal diagnostics, merged with syntax and schema ones
onDiagnosticsChange((diagnostics: CodeEditorDiagnostic[]) => void) | undefinedFired when the merged diagnostics change
apiRefRef<CodeEditorApi> | undefinedImperative handle
cspNoncestring | undefinedNonce for the style tags the editor injects
onCopy((value: string) => void) | undefinedFired by CodeSnippetCopyButton after a copy, with the current document
titlestring | undefined
defaultCheckedboolean | undefined
autoFocusboolean | undefined
idstring | undefined
tabIndexnumber | undefined
relstring | undefined
data-testidstring | undefined
size{ sm: string; md: string; lg: string; }

CodeEditorContent

PropTypeDefaultDescription
lineNumbersboolean | undefinedfalseShow the line-number gutter (CodeSnippet's <CodeSnippetLineNumbers /> equivalent)
titlestring | undefined
defaultCheckedboolean | undefined
autoFocusboolean | undefined
idstring | undefined
tabIndexnumber | undefined
relstring | undefined
data-testidstring | undefined

CodeEditorRoot

PropTypeDefaultDescription
valuestring | undefinedControlled document
defaultValuestring | undefinedUncontrolled initial document (controlled vs uncontrolled is locked on first render)
onChange((value: string) => void) | undefinedFired for user edits only, never for external value syncs
languageCodeEditorLanguage | undefinedtextPassed verbatim to the syntax adapter; selects the structural parser
documentIdstring | undefinedDocument identity: changing it swaps to that document's cached state (undo history, selection, folds). Requires a controlled value: when the id changes, value must already hold that document's text (an uncached id starts from value). In uncontrolled mode the new document would be seeded with the previous document's text.
readOnlyboolean | undefinedfalseNon-editable and unfocusable content; text remains selectable, searchable and copyable
startingLineNumbernumber | undefined1Offsets gutter numbers; lines, folds, diagnostics use absolute numbers
linesRecord<number, LineConfig> | undefinedEMPTY_LINESPer-line configuration keyed by absolute line number (static, see spec D6). Compared by identity: memoise it (or hoist it to a constant) — a new object every render reconfigures the lines and gutters on every render.
foldsCodeEditorFolds | undefinedFold regions, or a function re-run after edits; collapsed state is kept by id. Compared by identity: memoise the array or pass a stable function (e.g. getHttpFolds) — an inline array/arrow reconfigures folds on every render and bypasses the re-run debounce.
wrapLinesboolean | undefinedControlled line wrapping
defaultWrapLinesboolean | undefinedfalseUncontrolled initial line wrapping
onWrapLinesChange((wrap: boolean) => void) | undefinedFired when the wrap button toggles wrapping
maxLinesnumber | undefined0Height clamp in rows; auto-renders CodeSnippetShowMoreButton unless one is a direct child
originalstring | undefinedEnables diff mode against this text
schemaJsonSchema | undefinedJSON Schema for the document (json) or the JSON body (http)
completionsCodeEditorCompletionSource[] | undefinedEMPTY_COMPLETIONSConsumer completion sources, added after the built-in ones. Compared by identity: a new array reconfigures autocomplete and closes an open list, so keep it stable (module constant or useMemo).
diagnosticsCodeEditorDiagnostic[] | undefinedEMPTY_DIAGNOSTICSExternal diagnostics, merged with syntax and schema ones
onDiagnosticsChange((diagnostics: CodeEditorDiagnostic[]) => void) | undefinedFired when the merged diagnostics change
apiRefRef<CodeEditorApi> | undefinedImperative handle
cspNoncestring | undefinedNonce for the style tags the editor injects
onCopy((value: string) => void) | undefinedFired by CodeSnippetCopyButton after a copy, with the current document
titlestring | undefined
defaultCheckedboolean | undefined
autoFocusboolean | undefined
idstring | undefined
tabIndexnumber | undefined
relstring | undefined
data-testidstring | undefined
size{ sm: string; md: string; lg: string; }sm