Logo VitNode
Documentation

Editor

Rich text editor built on TipTap with a one-row toolbar, slash commands, emoji, panels and task lists, storing plain HTML.

Preview

Type / for blocks or : for emoji. Your fingers never leave the keyboard, and your coffee stays safe.

Usage

import { z } from 'zod'
import { AutoForm } from '@vitnode/core/components/form/auto-form'
import { AutoFormEditor } from '@vitnode/core/components/form/fields/editor'

const formSchema = z.object({
  content: z.string().min(1, 'Write something first'),
})
<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: 'content',
      component: (props) => <AutoFormEditor {...props} label="Announcement" />,
    },
  ]}
/>

Toolbar

One row, even in a narrow form. Everyday tools sit up front, the rest in menus:

  • Text style: paragraph or heading 1–4.
  • Bold, italic and …: underline, strikethrough, inline code, alignment, Clear formatting.
  • Text color, lists (bulleted, numbered, task), link, emoji, table, code block.
  • + Insert: quote, code block, table, divider, emoji, audio and panels.

Slash commands

Type /, keep typing to filter (/tab finds Table), pick with the arrow keys and Enter. Search matches translated names and English keywords, so /table works in every language.

Emoji

Type : and a few letters (:rocket) to get suggestions, or use the toolbar picker. Add your own emoji for every editor in vitnode.config.ts:

src/vitnode.config.ts
export const vitNodeConfig = buildConfig({
  editor: {
    emojis: [
      {
        label: 'VitNode',
        emojis: [
          { name: 'vitnode', src: '/logo.svg', tags: ['logo', 'brand'] },
        ],
      },
    ],
  },
})

name is the shortcode (:vitnode:), tags are extra search words, and label heads the section in the picker.

Panels

Callouts in four kinds: info, warning, error, success. One paragraph is a muted note. Add a second and the first becomes the title.

Insert one from + Insert or /panel. Picking the same kind again removes it; another kind switches the color. Panels are saved as plain HTML, <div class="tiptap-panel" data-panel="info">, so EditorContent renders them as is.

Task lists

Each item stores its checkbox state, so acceptance criteria stay ticked after saving. Start one from the toolbar, /task, or type [ ] at the start of a line.

Keyboard shortcuts

ActionShortcut
Submit the formCtrl/⌘ + Enter
Bold / italicCtrl/⌘ + B / I
UnderlineCtrl/⌘ + U
Bulleted listCtrl/⌘ + Shift + 8
Numbered listCtrl/⌘ + Shift + 7
Task listCtrl/⌘ + Shift + 9
Undo / redoCtrl/⌘ + Z / Shift + Z

Ctrl/⌘ + Enter submits the surrounding form, like its submit button. Outside a form it does nothing.

Accessibility

  • The toolbar is one tab stop. ← → move between buttons, Home / End jump to the ends.
  • In AutoForm, the writing area is named by the label, described by the description and error, and outlined red when invalid.
  • The / and : menus announce the highlighted option, and open upward when there's no room below.
  • A same-size placeholder holds the spot while the editor loads, so the page doesn't jump.

Multi-language

Add multiLang to edit the content in every enabled language. A language select (shown with two or more languages) switches the language being edited, not the app locale.

import { multiLangValueSchema } from '@vitnode/core/lib/helpers/multi-lang'

const formSchema = z.object({
  content: multiLangValueSchema({ minLength: 1 }).min(1),
})
<AutoFormEditor {...props} label="Content" multiLang />

The value becomes { languageCode, value }[], matching core_languages_words:

const value = [
  { languageCode: 'en', value: '<p>Hello</p>' },
  { languageCode: 'pl', value: '<p>Cześć</p>' },
]

Save it with saveLanguageWords. See Auto Form - Multi-language fields.

Rendering content

EditorContent renders stored HTML read-only, for public pages. It sanitizes the HTML first, so a sneaky <script> never runs.

import { EditorContent } from '@vitnode/core/components/ui/editor-content'

;<EditorContent content={post.content} />

Props

Prop

Type