Forms

Editor

A rich text editor that stays out of the way: a calm toolbar, a menu above selected text, blocks behind a slash, Markdown shortcuts, and clean HTML or JSON out.

Post
Copy
<script setup lang="ts">
const content = ref(`<h2>Release notes</h2>
<p>This release brings <strong>preview environments</strong> to every plan. Select text to format it, press <code>/</code> on an empty line for blocks, or use <code>⌘K</code> to add a link.</p>
<ul data-type="taskList">
  <li data-type="taskItem" data-checked="true"><p>Build every pull request</p></li>
  <li data-type="taskItem" data-checked="false"><p>Comment on previews</p></li>
</ul>
<blockquote><p>Review the real thing, not a screenshot.</p></blockquote>`)
</script>

<template>
  <JEditor v-model="content" label="Post" show-count />
</template>

Installation #

With the Nuxt module, every component is auto-imported — there is nothing to add. In a plain Vue project, import what you use:

Copy
import { JEditor } from '@juxtui/ui/editor'

Usage #

Copy
<script setup lang="ts">
import { JEditor } from '@juxtui/ui/editor'

const content = ref('<p>Hello</p>')
</script>

<template>
  <JEditor v-model="content" label="Post" />
</template>

The editor is its own entry point, @juxtui/ui/editor, so apps that don’t use it never load its engine. With the Nuxt module, JEditor is auto-imported like everything else.

Under the hood it runs on Tiptap (ProseMirror), which handles the hard parts of editing: undo history, pasting from Word or Google Docs, IME input and nested lists. The interface, keyboard model and styling are juxt.ui.

Markdown shortcuts work while typing: # to ### for headings, - or * for lists, 1. for numbered lists, [ ] for to-dos, > for quotes, three backticks for code and --- for a divider.

Pasted and linked URLs are cleaned up: javascript: links are refused, bare domains get https://, and email addresses become mailto: links.

Variants & examples #

Comment box#

toolbar="minimal" keeps only marks, link and lists. Slash commands are off.

Field with a limit#

Label, description and error work like Input. limit counts characters and stops typing at the limit.

Bio

Shown on your public profile.

Copy
<script setup lang="ts">
const bio = ref('<p>Designer and engineer. Builds calm software.</p>')
</script>

<template>
  <JEditor
    v-model="bio"
    label="Bio"
    description="Shown on your public profile."
    toolbar="minimal"
    :limit="160"
    min-height="6rem"
  />
</template>

Read-only#

Render stored content with the same typography, without a toolbar.

Copy
<template>
  <JEditor
    model-value="<h3>Terms</h3><p>By continuing you agree to the <a href='#'>terms of service</a>. This document can’t be edited.</p>"
    readonly
    :toolbar="false"
    min-height="0"
  />
</template>

API #

Props

PropTypeDefault Description
modelValuestring | JSONContent''Content, bound with v-model. An empty document is an empty string.
format'html' | 'json''html'Shape of v-model: HTML, or ProseMirror JSON for structured storage.
labelstring—Visible label; names the editing area.
descriptionstring—Supporting text under the editor.
errorstring | boolean—Error message, or true to mark it invalid.
placeholderstring'Write something, or type / for blocks…'Shown while the document is empty.
toolbar'full' | 'minimal' | false'full'Which toolbar to show.
bubbleMenubooleantrueFormatting menu above selected text.
slashCommandsbooleantrueType / on a line for a menu of blocks.
limitnumber—Maximum characters. Shown in the footer and enforced while typing.
showCountbooleanfalseWord and character count in the footer.
minHeightstring'10rem'Height of the writing area before it grows.
maxHeightstring—Height after which the writing area scrolls.
readonlybooleanfalseShow content without editing.
disabledbooleanfalseNot editable, visually muted.
requiredbooleanfalseMarks the field as required.
autofocusboolean | 'start' | 'end'falseFocus on mount.

Events

EventPayload Description
update:modelValuestring | JSONContentOn every change.
readyEditorThe Tiptap editor instance, for advanced use.
focus, blur—The writing area gained or lost focus.

Slots

SlotProps Description
toolbar-end{ editor }Extra controls before undo and redo.
label—Custom label content.

Methods

MethodSignature Description
focus(position?)(position?: 'start' | 'end') => voidFocus the writing area.
clear()() => voidEmpty the document.
editorEditorThe underlying Tiptap editor.

Accessibility #

  • The writing area is role="textbox" with aria-multiline, labelled by the field label and described by its description or error.
  • The toolbar is a single tab stop with arrow-key navigation (role="toolbar"). Toggle buttons expose aria-pressed; every button has a label and a tooltip with its shortcut.
  • While the slash menu is open, the textbox points at it with aria-controls and aria-activedescendant, so screen readers announce the highlighted block while focus stays in the text.
  • Clicking toolbar buttons never steals the selection, so formatting applies to what you selected.
  • To-do checkboxes are real checkboxes, operable with Space.

Keyboard #

Key Action
⌘BBold. Also ⌘I italic, ⌘U underline, ⌘E inline code, ⌘⇧S strikethrough, ⌘⇧H highlight.
⌘KAdd or edit a link at the selection. Enter applies, Esc cancels.
/On a line: open the block menu. ↑ ↓ to choose, Enter or Tab to insert, Esc to close.
TabIn a list: indent. Shift Tab outdents.
⌘ZUndo. ⌘⇧Z redoes.
←→In the toolbar: move between tools. Tab leaves the toolbar.