Skip to content

CodeEditor

WxCodeEditor is the field for code: JSON patches, a snippet of CSS, a Blade template, a YAML config. It is built on CodeMirror 6 — MIT, the same lineage as the ProseMirror under RichText — and the model is a plain string. Syntax highlighting, line numbers, folding, bracket matching, undo history and search (Ctrl+F) come with it; the colours are the design tokens, so both themes are covered.

json
Valid JSON

Usage

vue
<script setup lang="ts">
import { ref } from 'vue'

const patch = ref('{}')
</script>

<template>
  <wx-form-item label="Patch" name="patch">
    <wx-code-editor v-model="patch" language="json" />
  </wx-form-item>
</template>

Like every form control it takes size, status and disabled from the enclosing WxFormItem and WxForm, and a 422 from the server lands under the field as usual.

Languages

json, javascript, typescript, html, css, markdown, yaml, php and plain — the languages an admin panel is likely to hold, and nothing else: each one is a parser that ships with the component. plain is a monospace surface with the same chrome and no highlighting.

php understands the mixed HTML that a Blade template is, so it is the language to pick for one.

JSON is checked as you type

With language="json" the document is parsed after each pause in typing. A syntax error is underlined, the message shows on hover, and the lint event reports the same list — empty when the document is valid:

vue
<script setup lang="ts">
import { ref } from 'vue'
import type { CodeEditorDiagnostic } from '@webx-ui/core'

const problems = ref<CodeEditorDiagnostic[]>([])
</script>

<template>
  <wx-code-editor v-model="patch" language="json" @lint="problems = $event" />
  <wx-button :disabled="problems.length > 0">Apply</wx-button>
</template>

Each diagnostic carries from, to (character offsets), severity and message. Set :lint="false" to turn the check off. Other languages have no linter yet; the flag does nothing there.

format() re-indents a JSON document with tabSize spaces and returns true; when the text does not parse it returns false and leaves it alone, so a "Format" button never destroys what the user was in the middle of typing:

vue
<template>
  <wx-code-editor ref="editor" v-model="patch" language="json" />
  <wx-button @click="editor?.format()">Format</wx-button>
</template>

Keyboard

Tab indents and Shift+Tab dedents, as in any editor — which means Tab does not leave the field. Press Escape first and the next Tab moves focus on; screen readers announce this. Ctrl+F opens search inside the editor, Ctrl+Z / Ctrl+Shift+Z undo and redo, and Ctrl+/-style comment toggling is not wired because the languages disagree about what a comment is.

Height

The editor starts at minHeight and grows with the document. Give it maxHeight and it stops growing there and scrolls inside instead — the right choice inside a dialog, where the page must not become the thing that scrolls.

Beyond the props

extensions accepts any CodeMirror extensions and appends them to the editor's own: a custom linter, autocompletion for a known vocabulary, a keybinding. view — the EditorView — is exposed for anything else. Both are escape hatches; if a need keeps coming up, it belongs in a prop.

Props

PropTypeDefaultDescription
modelValuestring''The document
languageCodeEditorLanguage'plain'What to highlight as
placeholderstringShown while the document is empty
lineNumbersbooleantrueLine numbers and the fold gutter
lineWrappingbooleanfalseWrap long lines instead of scrolling sideways
tabSizenumber2Indentation width; Tab inserts this many spaces
lintbooleantrueRun the language's linter, where there is one
extensionsExtension[][]Extra CodeMirror extensions
minHeightstring'160px'Height before the editor starts growing
maxHeightstringHeight at which it scrolls instead
size'sm' | 'md' | 'lg''md'Control size
status'default' | 'success' | 'warning' | 'error''default'Validation state
disabledbooleanfalseNot editable and skipped by Tab
readonlybooleanfalseNot editable, but focusable and scrollable
idstringgeneratedOverrides the id the label points at; WxFormItem supplies one
ariaLabelstringLabel when there is no visible one

Events: update:modelValue (string), change (string), focus, blur, lint (CodeEditorDiagnostic[]).

Exposed: view — the CodeMirror EditorView — plus focus() and format().

Bundle size

CodeMirror and its language packages are dependencies of @webx-ui/core and are installed with it. They are not bundled into our output, so an app that never imports WxCodeEditor will not ship them. Watch for a second copy of @codemirror/state: if the app installs CodeMirror itself at another version, the editor throws Unrecognized extension value — dedupe it in the bundler.

Released under the MIT License.