@abraca/nuxt
v1.6.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.
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 30+ built-in extensions
- Built-in page renderers: Kanban, Table, Calendar, Gallery, Outline
- 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
@abraca/dabra>= 1.0.0@tiptap/core,@tiptap/vue-3,@tiptap/extension-collaboration,@tiptap/extension-collaboration-caretyjs>= 13
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) |
| useYDoc(ydoc) | Reactive Yjs primitives — useSyncedMap, useSyncedArray, useAwarenessOf, useSmoothedCursors |
| 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
pnpm dev
# Build
pnpm prepack
# Lint
pnpm lint
# Test
pnpm test
pnpm test:watch