---
title: "Register the Nuxt module"
description: "Register @skaile/forge-editor/nuxt in nuxt.config.ts, what the module wires into build, client and server, and which routes it mounts."
canonical_url: "https://skaile-ai.github.io/forge-editor/guides/nuxt-module"
---
# Register the Nuxt module

> Register @skaile/forge-editor/nuxt in nuxt.config.ts, what the module wires into build, client and server, and which routes it mounts.

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

```ts
// 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`:

```ts
// 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()`.


## Sitemap

See the full [sitemap](https://skaile-ai.github.io/forge-editor/sitemap.md) for all pages.
