Glossen

Install on Next.js

Add the Glossen UI and the Next.js backend starter to a Fumadocs app.

This guide installs the UI and the complete backend into a Next.js App Router project that already uses Fumadocs with Fumadocs MDX. If you already have a chat API, follow Keep your own backend instead.

Add a components.json

The shadcn CLI needs a components.json file to know where to put files. Fumadocs apps don't have one by default.

Create it by hand rather than running shadcn init. init rewrites your global CSS with a shadcn theme, which Glossen does not need.

components.json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "",
    "css": "app/global.css",
    "baseColor": "neutral",
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/cn",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  }
}

aliases.utils must point to a module that exports a cn class-name helper. Apps created with create-fumadocs-app already have lib/cn.ts. If your project uses @/lib/utils, use that instead.

If you already have a components.json, keep it.

Install the UI and the backend

pnpm dlx shadcn@latest add https://glossen.vercel.app/r/glossen.json https://glossen.vercel.app/r/glossen-next.json

This adds:

FilePurpose
components/glossen/*The chat panel, trigger, selection button, conversation store and Markdown renderer
lib/glossen/chat.tsThe answer generation: documentation loading, prompt, model call, sources and safe errors
app/api/chat/route.tsA thin App Router route that calls lib/glossen/chat.ts

If app/api/chat/route.ts already exists, the CLI asks before replacing it. Answer no to keep yours and point Glossen at another endpoint with the endpoint option.

Add your OpenRouter key

The CLI adds these variables to .env.local. Fill in the key:

.env.local
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_MODEL=openrouter/free

The key is only read on the server. See Model and provider to use another model or provider.

Enable processed Markdown

The backend reads each page with page.data.getText("processed"). Turn this on in Fumadocs MDX:

lib/source.ts
const docs = defineDocs({
  dir: "content/docs",
  docs: {
    postprocess: {
      includeProcessedMarkdown: true,
    },
  },
})

export const source = loader({
  baseUrl: "/docs",
  source: docs.toFumadocsSource(),
})

New Fumadocs apps already include this. lib/glossen/chat.ts imports source from @/lib/source. If your loader lives somewhere else, change that import.

Mount the provider

Wrap your app in GlossenProvider inside Fumadocs' RootProvider. It lives in the root layout so conversations keep streaming while readers move between pages.

app/layout.tsx
import { RootProvider } from "fumadocs-ui/provider/next"
import { GlossenProvider } from "@/components/glossen"

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <RootProvider>
          <GlossenProvider
            suggestions={["How do I get started?", "What does this library do?"]}
          >
            {children}
          </GlossenProvider>
        </RootProvider>
      </body>
    </html>
  )
}

Add the panel to the docs layout

app/docs/layout.tsx
import { DocsLayout } from "fumadocs-ui/layouts/docs"
import { AskAIPanel, AskAITrigger } from "@/components/glossen"
import { baseOptions } from "@/lib/layout.shared"
import { source } from "@/lib/source"

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <DocsLayout tree={source.getPageTree()} {...baseOptions()}>
      {children}
      <AskAIPanel />
      <AskAITrigger />
    </DocsLayout>
  )
}

Run pnpm dev, open a docs page and click Ask AI, or press ⌘/.

Route duration

The starter sets export const maxDuration = 60 so long answers have time to finish. Your host's plan limits still apply; raising the value gives answers more time but doesn't guarantee they finish.

Make custom components readable

Glossen sends the processed Markdown of every page. Fumadocs converts MDX to Markdown, but custom components with no text content produce nothing the model can read. Keep important information in text, or make your components render meaningful children. You can check what the model sees at /llms-full.txt in new Fumadocs apps.

Troubleshooting

ERR_PNPM_IGNORED_BUILDS during install

pnpm 11 and later won't add packages while a dependency's build script is waiting for approval. New Fumadocs apps ship esbuild and unrs-resolver in that state, so shadcn add stops before writing any files.

Approve the packages listed in the error in pnpm-workspace.yaml, then run the install command again:

pnpm-workspace.yaml
allowBuilds:
  esbuild: true
  unrs-resolver: true

pnpm approve-builds does the same interactively.

On this page