Register the Nuxt module
Nuxt apps use the same package entries as every other host; the module only adds the build and server wiring and one client plugin.
Register the module
// nuxt.config.ts
export default defineNuxtConfig({
// @nuxt/ui first: the editor module patches its generated `ui.css` template.
modules: ['@nuxt/ui', '@skaile/forge-editor/nuxt'],
tiptapEditor: { ai: { /* see the AI configuration guide */ } },
})
Add "@skaile/forge-editor" to the host's dependencies (workspace:* in this repository, the registry version elsewhere). The package ships built ESM (dist); the module puts it into build.transpile for the client and Nitro's externals.inline for the server so the runtime files in dist/nuxt/runtime (they import h3 and nitropack/runtime for the server and #app for the client explicitly) are compiled by the host. Because the package is transpiled, Vite does not pre-bundle it; the module therefore adds the TipTap/ProseMirror/Yjs entries the editor imports to vite.optimizeDeps.include (merged with the host's list), so they share one ProseMirror with the pre-bundled @tiptap/* deps instead of failing with RangeError: Adding different instances of a keyed plugin. h3 and nitropack are optional peers: a Nuxt app already provides them; only a strictly isolated install (pnpm without hoisting) may need them listed next to the package.
The module registers exactly one client plugin (src/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts, which hands nuxtUiKit() to the context; the module has no kit option): it installs the editor context with account-synced settings and the public AI configuration. It auto-registers no component or composable: import everything from the package entries.
The Nuxt module's server plugin installs the routes at the first request, after every Nitro plugin has run, so the host's provideEditorServer({ ports }) call (@skaile/forge-editor/server/nitro) may sit in any plugin; host routes then reach the installed server with useEditorServer() (forgetDocument, liveContent, enforceRateLimit, resolveModel). Without provided ports the first request logs “@skaile/forge-editor: provideEditorServer() was not called” and the routes stay unmounted until a request succeeds in installing them. A route whose port the host did not register is not mounted at all (the path answers 404): documents routes always exist, settings needs the settings port, users the directory port, assets, themes and comments their ports, and tags follows from documents. The AI routes (/api/ai/*) are mounted by the module always, by a plain Nitro app when it passes ai. The collaboration websocket is mounted when the collabState port is present.
Without an editor server
A host that serves no editor routes (a client-only editor, or a host with its own /api/editor/*) sets server: false:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/ui', '@skaile/forge-editor/nuxt'],
tiptapEditor: { server: false },
})
The module then adds only the build wiring: the stylesheets, build.transpile, the Vite pre-bundling, the Tailwind @source and the public AI configuration. It mounts no routes and no websocket, sets no server config and registers no client plugin, because that plugin's HTTP backend talks to the module's routes. The host installs the editor context itself (createEditor from @skaile/forge-editor/vue) with the backend it has, and calls no provideEditorServer().
Production
Build and run a Nuxt host with the editor module: runtime secrets, persistent storage and shutdown behaviour.
Storage adapters, comment stores, Nitro host
Pick where documents live with ready-made EditorPorts adapters, comment stores, a Nuxt-free Nitro host and the useEditorDocument composable.