@abraca/nuxt
v2.37.0
Published
First-class Nuxt module for the Abracadabra CRDT collaboration platform
Readme
@abraca/nuxt
First-class Nuxt 4 module for the Abracadabra CRDT collaboration platform. Drop real-time collaborative documents, multi-user awareness, offline-first sync, and a full rich-text editor into any Nuxt app.
Documentation
Full, code-derived documentation lives in docs/ — a numbered Nuxt-Content
site, the single source of truth (prior AUDIT.md/audit-*.md/gaps/ were untrusted
and moved to ../DEPRECATED/abracadabra-nuxt/; where this README or CLAUDE.md drifted,
the docs win). Seven sections:
| Section | Covers |
|---|---|
| Getting Started | install, peers, the exhaustive options schema, quick start |
| Core | setup() internals, client/server bootstrap, plugins & registry, state & types, Nitro RPC runners |
| Composables | all ~107 auto-imported composables (incl. the useYDoc.ts Yjs primitives — there is no useYDoc()) |
| Components | the 210 components (44 top-level + 166 across eight subdirs), incl. the 52 Aware components |
| Editor Extensions | the ~41 collaborative TipTap nodes/marks/behaviours |
| Guides | doc page, plugin, RPC method, theming, auth, custom renderer, i18n |
| Reference | utils, awareness sharing modes, server API routes, gotchas |
Features
- Real-time collaborative editing via WebSocket + Yjs CRDT
- Offline-first with IndexedDB persistence and background sync queue
- Multi-user awareness — cursors, presence, typing indicators, voice/video
- TipTap rich-text editor with ~41 built-in collaborative extensions
- 15+ built-in page renderers: doc/prose/code, Kanban, Table, Calendar, Gallery, Outline, Checklist, Timeline, Call, Graph, Dashboard, Map, Slides
- Full-text search with trigram indexing
- File uploads with offline blob caching
- Passkey (WebAuthn) authentication
- Server-side rendering with Nitro plugin system and doc cache
- Extensible plugin architecture
Requirements
- Nuxt >= 4, Vue >= 3.4,
@nuxt/ui>= 3 @abraca/dabra,@abraca/convert,@abraca/plugin(all^2.3)@tiptap/* 3collaboration set,lowlight,@noble/ed25519,@noble/hashesyjs>= 13
See Getting Started → Installation for the full required/optional peer matrix.
Setup
pnpm add @abraca/nuxt @abraca/dabra yjs @tiptap/core @tiptap/vue-3 @tiptap/extension-collaboration @tiptap/extension-collaboration-caret// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@abraca/nuxt'],
abracadabra: {
url: 'https://your-abracadabra-server.example.com',
}
})Wrap your app with <AProvider> and you're live:
<template>
<AProvider>
<NuxtPage />
</AProvider>
</template>Configuration
abracadabra: {
// Connection
url?: string // Server URL (default: 'https://abra.cou.sh')
entryDocId?: string // Root doc ID (skips space discovery)
// Auth
persistAuth?: boolean // Persist JWT in localStorage (default: true)
authStorageKey?: string // localStorage key (default: 'abracadabra:auth')
auth?: {
middleware?: boolean // Register abracadabra-auth middleware
globalMiddleware?: boolean // Apply to all routes
loginPath?: string // Redirect target (default: '/login')
}
// Features (control what code is included)
features?: {
editor?: boolean // TipTap editor (default: true)
search?: boolean // Full-text search (default: true)
backgroundSync?: boolean // Offline sync queue (default: true)
renderers?: boolean // Built-in page renderers (default: true)
serverCache?: boolean // Server doc-tree cache runner (default: true)
}
// UI
prefix?: string // Component name prefix (default: '')
locale?: Partial<AbracadabraLocale>
docBasePath?: string // Doc route base (default: '/doc')
// Server-side service account (for Nitro runners)
service?: {
publicKey?: string // Ed25519 public key (base64url)
privateKey?: string // Ed25519 private key (base64url)
}
debug?: boolean
}Auto-imported Composables
| Composable | Description |
|---|---|
| useAbracadabra() | Full collaboration state (client, provider, identity, docs) |
| useAbracadabraAuth() | Auth — login, passkey, identity management |
| useAwareness() | All connected users' raw awareness states |
| useAwarenessPeers() | Typed peer list with idle detection |
| useAAField(key) | Per-field interaction awareness (hover, press, focus) |
| useSyncedMap / useSyncedArray / useSyncedText / useSyncedXml / useAwarenessOf / useSmoothedCursors | Reactive Yjs primitives (the useYDoc.ts helper module — there is no useYDoc() and no useMultiplayerObject) |
| useEditor(ydoc, opts) | TipTap setup with collaboration + cursor extensions |
| useChildTree(docId) | Document tree scoped to a parent |
| useDocumentPermissions(role) | RBAC flags (canWrite, canDelete, etc.) |
| useChat(channel) | Real-time group & DM chat |
| useNotifications() | Server-pushed notifications |
| useSearchIndex() | Full-text search over synced docs |
| useBackgroundSync() | Offline sync queue status |
| useDocExport() | Export docs as markdown, HTML, or ZIP |
| useDocImport() | Import .md, .txt, .html files |
| useFileBlobStore() | Offline file blob cache (IndexedDB) |
| useVoice() | WebRTC voice/video state |
| useWindowManager() | Floating window (PiP/panel) management |
| useCommandPalette() | ⌘K command palette state & commands |
| useConnectionStatus() | Connection status as UI-ready label + color |
| useRendererBase(docId) | Shared setup for custom page renderers |
| useTrash() | Document trash (move, restore, permanent delete) |
| usePasskeyAccounts() | WebAuthn passkey account management |
| useFollowUser(userId) | Cursor following for pair programming |
Auto-imported Components
Core
| Component | Description |
|---|---|
| <AProvider> | Root wrapper — initializes SDK and syncs root doc |
| <AEditor> | Full TipTap collaborative editor |
| <ADocumentTree> | Hierarchical doc tree with drag-drop |
| <ANodePanel> | Slide-over panel with doc metadata and children |
| <ACommandPalette> | ⌘K search and command palette |
| <AConnectionStatus> | Connection state badge |
| <APresence> | Connected users presence indicator |
| <ANotifications> | Notification center |
| <AVoiceBar> | Voice/video call controls |
| <ADocTypeSelect> | Page type selector |
| <APermissionGuard> | Conditional render based on role |
| <ARoleBadge> | Role badge (Owner, Editor, Viewer, etc.) |
| <AFloatingWindow> | Floating/PiP window container |
| <AWindowLayer> | Portal for floating windows |
Aware Interaction Components
Interaction components that broadcast awareness state to other users:
<AArea> <AAvatar> <AButton> <AInput> <ATextarea> <ASelect> <ACursorLabel> <AFacepile> <AUserList> <ADocBadge>
Page Renderers (requires features.renderers)
<AKanbanRenderer> <ATableRenderer> <ACalendarRenderer> <AGalleryRenderer> <AOutlineRenderer>
Server-Side Usage
Configure a service account for server-side doc access:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@abraca/nuxt'],
runtimeConfig: {
abracadabra: {
servicePublicKey: process.env.ABRA_SERVICE_PUBLIC_KEY,
servicePrivateKey: process.env.ABRA_SERVICE_PRIVATE_KEY,
}
}
})Register custom server runners via the Nitro hook:
// server/plugins/my-runner.ts
export default defineNitroPlugin((nitro) => {
nitro.hooks.hook('abracadabra:before-runners', (ctx) => {
registerServerPlugin({
name: 'my-runner',
runners: [{
name: 'my-background-task',
run: async ({ provider, client }) => {
// background work
return () => { /* cleanup */ }
}
}]
})
})
})Plugin System
Extend the editor, add toolbar items, slash commands, and page types:
import type { AbracadabraPlugin } from '@abraca/nuxt'
const myPlugin: AbracadabraPlugin = {
name: 'my-plugin',
extensions: () => [/* TipTap extensions */],
toolbarItems: () => [/* toolbar groups */],
suggestionItems: () => [/* slash commands */],
pageTypes: [{ type: 'my-type', label: 'My Page', component: MyRenderer }],
clientSetup: async (state) => { /* init */ }
}Contribution
# Install dependencies
pnpm install
# Generate type stubs
pnpm dev:prepare
# Develop with the playground (one route per module feature)
pnpm dev
# Build
pnpm prepack
# Lint
pnpm lint
# Unit + integration tests (requires a running abracadabra-rs at $ABRACADABRA_URL for integration)
pnpm test
pnpm test:watch
pnpm test:integration
# Browser E2E (Playwright against a real spawned abracadabra-rs + abracadabra-aperio)
pnpm test:e2e # headless
pnpm test:e2e:headed # headed debug
pnpm test:e2e:ui # Playwright UI modeE2E setup
pnpm test:e2e drives Chromium against a freshly-spawned abracadabra-rs
(release binary) and abracadabra-aperio (Nuxt dev) — no mocks. The sibling
repos must be checked out next to this one:
Abracadabra/
├── abracadabra-nuxt/ (this repo)
├── abracadabra-rs/ # build: cargo build --release -p abracadabra-server
└── abracadabra-aperio/ # consumer app under testThe test harness listens on port 3300 for aperio. Keep that free. Set
E2E_VERBOSE=1 to stream rs + aperio logs alongside test output — useful
when a test is surprising you.
The playground/ directory is a focused, multi-page sandbox — one route per module feature — used to develop and visually verify the module in isolation. For a full reference application that exercises the entire module surface (auth, spaces, chat, file transfer, settings, etc.), see abracadabra-aperio, the open-source dashboard built on top of @abraca/nuxt.
