@ecsplico/editor

Vue and React editor library on TipTap v3 (Vue: Nuxt UI; React: shadcn/ui): framework-free features with Vue and React bindings, simple and advanced presets, tree-shakeable entries. Built ESM with type declarations.
1,098
Install
npm install @ecsplico/editor
Latest Version:4.2.0
Vue:3
License:MIT
Last Updated:Oct 22, 2026
Links: npm
Maintainer: mortegro

@ecsplico/editor

A Vue and React editor library on TipTap v3. Editors are assembled from features: the simple editor is the core features alone (formatting, headings, lists, quotes, code, undo/redo) and needs no backend, no Yjs and no feature code; the advanced editor composes every feature (media, mentions, transcripts, composites, authors, comments, AI, collaboration, block UI, site-wide Obsidian themes and their integrations) on both surfaces. The framework-free ./core holds the feature model, and every feature's logic is framework-free too (./<feature>): a feature is built from a typed UI binding (<x>FeatureCore(ui)) that the Vue layer (./<feature>/vue, Nuxt UI chrome) and the React layer (./<feature>/react, shadcn/ui on Radix and Tailwind) fill with their components, node views and notice seam. Toolbar, settings panel and modal, outline, AI sidebar, comments margin, composite table and inspector, and the file browser are components you place anywhere beside an editor; both layers ship the simple editor (./vue/simple, ./react/simple) and the advanced editor (./vue/advanced, ./react/advanced).

Prerequisites

  • Node ≥ 22 and Bun (the repository is a Bun workspace: packages/*, examples/*).
  • A Vite- or Nuxt-based app (@vitejs/plugin-vue / @vitejs/plugin-react, or Nuxt). The published package is a built ESM package: every exports subpath resolves to dist/**/*.js with .d.ts (Vue SFCs are compiled, React .tsx is compiled to JSX-runtime JS), the CSS sheets are copied, and the source maps embed the sources (src itself is not published). Output is one module per source file, so entries stay tree-shakeable.
  • Required peer dependencies (install them in the consuming app so the host and the library share one instance of each): @tiptap/core, @tiptap/pm, yjs, @hocuspocus/provider. Yjs and the provider are required peers because shared prop types in the .d.ts reference them; a simple-editor bundle does not contain their implementation (proven below). Per layer (all optional peers): Vue vue, @nuxt/ui, @tiptap/vue-3; React react, react-dom, @tiptap/react, @iconify/react; code surface (/code) @codemirror/state, @codemirror/view, @codemirror/language, @codemirror/commands, @codemirror/search, @lezer/highlight, plus the @codemirror/lang-* packs you register (the package ships none).
  • Tailwind CSS 4 (tailwindcss) for the Nuxt UI theme (Vue) or the shadcn classes (React), and the icon collection @iconify-json/lucide. React hosts also need tw-animate-css (enter/exit animations of the dialog, dropdown and tooltip).

Install

From npmjs (public, no token and no .npmrc needed):

npm install @ecsplico/editor      # or: bun add @ecsplico/editor / pnpm add @ecsplico/editor

Inside this repository the package is a workspace member, so consumers there declare "@ecsplico/editor": "workspace:*".

Nuxt quick start

Install the peers next to the package (vue, @nuxt/ui, @tiptap/core, @tiptap/pm, @tiptap/vue-3, yjs, @hocuspocus/provider, tailwindcss, @iconify-json/lucide), then register the module after @nuxt/ui:

// nuxt.config.ts
export default defineNuxtConfig({
  ssr: false, // TipTap renders on the client only
  modules: ['@nuxt/ui', '@ecsplico/editor/nuxt'],
  css: ['~/assets/main.css'],
})
/* app/assets/main.css */
@import "tailwindcss";
@import "@nuxt/ui";

Wrap the app in <UApp> (app/app.vue: <UApp><NuxtPage /></UApp>) and mount an editor on a page (the simple editor needs no backend):

<script setup lang="ts">
import { SimpleEditor } from '@ecsplico/editor/vue/simple'
import { ref } from 'vue'
const content = ref()
</script>

<template>
  <SimpleEditor v-model="content" />
</template>

The module's API routes call the host's EditorPorts (identity first, then one port per capability: documents, collabState, directory, assets, settings, themes, models, comments). Register them in a Nitro plugin; without them the client logs „Einstellungen konnten nicht geladen werden“ and every route answers 500. A route whose port you leave out answers 501. These minimal ports keep everything in memory and let any visitor in (replace authenticate and the storage with yours):

// server/plugins/editor-adapter.ts
import type { JSONContent } from '@tiptap/core'
import { type AssetStore, setEditorPorts } from '@ecsplico/editor/server'

export default defineNitroPlugin(() => {
  const contents = new Map<string, JSONContent>()
  const states = new Map<string, Uint8Array>()
  const assets = new Map<string, Parameters<AssetStore['storeAsset']>[1]>()
  setEditorPorts({
    // `null` = 401 on API routes; on /collab the socket is closed with 4401 (HTTP 401 at the upgrade with `collab.authenticateUpgrade`)
    identity: { authenticate: async () => ({ id: 'demo', username: 'demo' }) },
    documents: {
      loadContent: async id => contents.get(id) ?? null,
      storeContent: async (id, content) => { contents.set(id, content) },
      listContents: async () => [...contents].map(([id, content]) => ({ id, content })),
    },
    collabState: {
      loadState: async id => states.get(id) ?? null,
      storeState: async (id, state) => { states.set(id, state) },
      removeState: async (id) => { states.delete(id) },
    },
    directory: { listUsers: async () => [] },
    assets: {
      storeAsset: async (id, asset) => { assets.set(`${id}/${asset.name}`, asset) },
      loadAsset: async (id, name) => {
        const asset = assets.get(`${id}/${name}`)
        if (!asset) return null
        const { data, ...record } = asset
        return { record, data }
      },
    },
  })
})

The advanced editor, every adapter method, collaboration and AI are covered in the Nuxt quick start and the EditorPorts guide.

Building and developing in this repository

bun run build:editor (tsdown, config packages/editor/tsdown.config.ts) writes packages/editor/dist: ESM plus .d.ts for every exports subpath (the entries are read from the skaile-source condition of package.json#exports, so a new export is built as soon as it is declared), unbundled one module per source file, source maps with embedded sources, CSS copied, and the Nuxt runtime directory (dist/nuxt/runtime: plugin, server routes, utils) as separate files next to the module that Nuxt and Nitro compile in the host. Dependencies and peers are never bundled.

Each exports entry carries a custom skaile-source condition first (pointing at src) followed by types/import (or default) pointing into dist. This repository's Nuxt app (nuxt.config.ts) and the examples (examples/*/vite.config.ts, tsconfig.json#customConditions) enable that condition for Vite, Nitro and the type checker, so editing the library needs no build step in dev. Consumers outside the repository never set the condition and get dist. bun run check:editor-entries builds first and judges the built output through exports (no skaile-source), so it proves what is published; bun run check:editor-composition builds first and loads the built entries; bun run typecheck:editor resolves through src (customConditions: ["skaile-source"] in packages/editor/tsconfig.json).

Documentation

The documentation lives at https://skaile-ai.github.io/skaile-editor/ (the package itself no longer ships guides):

  • Getting started: what the package is, installation, quick starts for Nuxt, plain Vue and React, glossary.
  • Tutorial: build one editor step by step, including an own block, the backend, AI and the React surface.
  • Guides: feature composition, Nuxt module, EditorPorts, collaboration, AI, themes, composites, file browser, production, storage adapters, migration.
  • Reference: every public export, presets and features, the feature contract, CSS and UI-provider requirements, dependency rules, commands, changelog.
  • AI agents: how coding agents read these docs, integrate and extend the editor, invariants and verification.

Machine-readable entry points: llms.txt, llms-full.txt, and an installable Agent Skill:

npx skills add https://skaile-ai.github.io/skaile-editor

Runnable examples live in the repository: examples/ and the Nuxt app (bun run dev, port 3056). To work on the documentation site from a checkout of the repository, run bun run docs:dev (development server) or bun run docs:build (static build); see docs/README.md.

Version History

Version Released Vue License
4.2.0 ^3.5.42 MIT
4.1.0 ^3.5.42 MIT
4.0.0 ^3.5.42 MIT
3.6.0 ^3.5.42 MIT
3.5.1 ^3.5.42 MIT
Pre-releases (1)
Version Released License
0.0.0-stage