Docusaurus
The ondocs/docusaurus plugin adds ondocs to a Docusaurus 3 site with one line of config. After every docusaurus build it indexes the pages the site serves, and in the browser it adds the dialog with a floating “Ask AI” button.
Live demo: these docs as a Docusaurus site, with the plugin, on Vercel.
Install the plugin
Section titled “Install the plugin”Install the package with the AI SDK, the OpenAI provider (the default embedding model) and the dialog’s two dependencies:
npm i ondocs ai @ai-sdk/openai @radix-ui/react-dialog cmdkAdd it to docusaurus.config.ts
Section titled “Add it to docusaurus.config.ts”To add ondocs to Docusaurus, put the plugin in the plugins array of docusaurus.config.ts:
export default { // … plugins: ['ondocs/docusaurus'],};The dialog posts questions to /api/ask on your site. Set endpoint for another path or host, as in ['ondocs/docusaurus', { endpoint: 'https://ask.example.com/api/ask' }]; an endpoint on another origin must send CORS headers for your site’s origin, as in Cross-origin requests. The ASK_ENDPOINT environment variable, when it is set as the site builds or starts, overrides both, which is how local development points the dialog at ondocs dev.
What the plugin indexes
Section titled “What the plugin indexes”After docusaurus build, the plugin indexes the docs, blog posts and MDX pages into build/ask-index.json, at the exact URLs Docusaurus generated, so citations never guess at a URL. It reads each page’s Markdown content without the navbar, breadcrumbs, table of contents, doc cards or pagination. Pages that only list other pages are left out: blog lists, tag and author pages, and generated category indexes.
A page with <meta name="robots" content="noindex"> is left out too, so with Docusaurus’s noIndex: true the index is empty. To keep a site out of search engines and still index it, send an X-Robots-Tag: noindex header from the host instead, such as from headers in vercel.json.
There is nothing to commit. The index is rebuilt with the site, and unchanged pages reuse their vectors from the previous build, cached in node_modules/.cache/ondocs, which Netlify and Vercel keep between builds. Leave sections out with exclude, for example exclude: ['/changelog'].
The dialog and the shortcut
Section titled “The dialog and the shortcut”In the browser the plugin adds a floating “Ask AI” button beside the back-to-top button, and opens the dialog with ⌘I or Ctrl+I. ⌘K stays with your search, and text fields keep ⌘I for italic. The dialog follows the site’s light or dark mode, and citations route through Docusaurus without a page reload.
DocSearch’s own Ask AI panel also opens with ⌘I. Pick another key with dialog: { shortcut: 'j' }, turn the shortcut off with shortcut: false, or hide the button with launcher: false. dialog: { links: 'sources' } keeps only the answer’s links to its source pages.
Ask AI in the Docusaurus navbar
Section titled “Ask AI in the Docusaurus navbar”dialog: { navbar: true } puts the “Ask AI” button in the navbar instead of the corner: after the search box, or where it would be without a search plugin, filled with your primary color and with the shortcut on it, ⌘I on a Mac and Ctrl I elsewhere. On a phone it is an icon that stays in the navbar beside search, not in the menu. Pass a string for another label, as in navbar: 'Ask the docs'; by default it is labels.launcher, in each locale. The floating button is off with it, unless you also set launcher: true.
plugins: [['ondocs/docusaurus', { dialog: { navbar: true } }]],The plugin renders the button from its own @theme/Navbar/Search, which wraps the classic theme’s, so it works with DocSearch, a local search plugin or none. If your site swizzles Navbar/Search, or wants the button somewhere else, give any element the data-ondocs-open attribute and it opens the dialog: see Open it from your own button.
Embeddings in Docusaurus
Section titled “Embeddings in Docusaurus”The plugin embeds with OpenAI’s text-embedding-3-small at 512 dimensions when OPENAI_API_KEY is set at build time, or with the same model through AI Gateway when only AI_GATEWAY_API_KEY is. Without either key it builds a keyword-only index and prints a warning. If a key is set but the provider cannot load, the build fails with the reason instead of quietly going keyword-only.
Choose the model with embedding, named the way the CLI names it, so the config imports no provider: 'openai:text-embedding-3-large', 'cohere/embed-v4.0' through AI Gateway, 'workers-ai:@cf/baai/bge-small-en-v1.5' for the Workers AI Worker, 'mock' for offline builds, or 'none' for keyword-only. dimensions sets the vector size for models that support it, and the endpoint reads it from the index.
plugins: [['ondocs/docusaurus', { embedding: 'openai:text-embedding-3-small', dimensions: 512 }]],Deploy the endpoint for Docusaurus
Section titled “Deploy the endpoint for Docusaurus”A Docusaurus site is static, so the endpoint runs as a function on your host and reads the index the build wrote. Write it with one command in the site’s folder:
npx ondocs initOn Vercel it writes api/ask.ts and api/mcp.ts and bundles build/ask-index.json with them in vercel.json; on Netlify and Cloudflare, their equivalents. On GitHub Pages, where Docusaurus sites often live and nothing runs but files, it writes a Cloudflare Worker that reads the index from the live site and answers with Workers AI, with no API key, and the dialog posts to it: set endpoint to its URL. See Workers AI. The endpoints on Vercel, Netlify and Cloudflare embed questions with OpenAI’s text-embedding-3-small, the plugin’s default; each has a rate limit and a daily budget. See Deploying for what it writes on each host.
Try the Docusaurus plugin locally
Section titled “Try the Docusaurus plugin locally”Build the site once, run npx ondocs dev in its folder, which answers from build/ask-index.json, and start the site with ASK_ENDPOINT=http://localhost:8787/api/ask npm start. Without a key, the build makes a keyword-only index unless you set embedding: 'mock', and ondocs dev answers with the mock model either way. docusaurus start reminds you when the dialog posts to a path it does not serve.
Docusaurus plugin options
Section titled “Docusaurus plugin options”| Option | Default | What it does |
|---|---|---|
endpoint |
ASK_ENDPOINT, else /api/ask |
Where the dialog posts questions; ASK_ENDPOINT overrides it. |
embedding |
OpenAI when a key is set | Model spec: openai:<model>, workers-ai:@cf/<model>, <provider>/<model>, mock, none. |
dimensions |
the model’s | Vector size, for models that support it. |
embeddingModel |
none | An AI SDK embedding model object, instead of embedding. |
embeddingProviderOptions |
none | Passed to the embedding model. |
chunking |
{ maxChars: 1200, overlap: 150 } |
Chunk size and overlap, in characters. |
indexFile |
ask-index.json |
Where the index is written in build/ and served from. |
exclude |
[] |
Path prefixes to leave out, relative to baseUrl. |
versions |
'last' |
Docs versions to index: the last one, or 'all'. |
dialog |
title, placeholder, suggestions, shortcut, launcher, navbar, labels, locales. |
|
mcp |
none | The MCP endpoint’s URL or path, for <OndocsMcp />. |
llmsTxt |
on | Write llms.txt, llms-full.txt and .md copies; false turns them off. |
Locales and versions
Section titled “Locales and versions”Docusaurus builds each locale on its own, and each gets its own index under its locale path: build/fr/ask-index.json, served at /fr/ask-index.json. On a locale’s pages, the dialog sends the locale with each question, and an endpoint with indexes answers from that locale’s index: npx ondocs init reads the locales from i18n and writes that. Give the dialog its words in each language with dialog.locales, by Docusaurus locale, over the dialog’s own options:
plugins: [ [ 'ondocs/docusaurus', { dialog: { suggestions: ['How do I install it?'], locales: { fr: { title: 'Demander à Acme', suggestions: ['Comment l’installer ?'], labels: { launcher: 'Demander', placeholder: 'Posez une question…', newQuestion: 'Nouvelle question' }, }, }, }, }, ],],Every label is listed in The ask dialog.
With versioned docs, the plugin indexes the last version, the one at /docs that readers land on, and leaves out the others, such as /docs/next and /docs/1.0, so answers cite the version people use. llms.txt and the .md copies follow the index. versions: 'all' indexes every version; to keep only some, add the others to exclude, as in versions: 'all', exclude: ['/docs/1.0']. When several indexed versions have a section word for word, search returns it once, from the shortest URL: see Copies of a section.
When another plugin wraps Root
Section titled “When another plugin wraps Root”When two plugins wrap Root, Docusaurus uses only the last one’s wrapper, so the dialog can go missing. Render it from your own Root instead; it shows once even if the plugin’s Root renders it too:
import Ondocs from '@theme/Ondocs';import Root from '@theme-original/Root';import type { ReactNode } from 'react';
export default function RootWithAsk({ children }: { children: ReactNode }) { return ( <Root> {children} <Ondocs /> </Root> );}