Model or dataset
supabase-community/nextjs-openai-doc-search avatar
supabase-community/nextjs-openai-doc-search

nextjs-openai-doc-search: a Supabase and OpenAI doc search starter

Template for building your own custom ChatGPT style doc search powered by Next.js, OpenAI, and Supabase.

1,731 stars312 forksTypeScriptApache-2.0

At a glance

What is it?
This starter turns the .mdx files in your pages directory into a ChatGPT-style documentation search. The build-time embeddings step and the runtime vector search are the two halves worth understanding before you adopt it.
Who is it for?
Adopt this starter if your documentation already lives as .mdx files inside a Next.js pages directory and you want a working retrieval-augmented search box rather than a general-purpose RAG framework. It is the wrong tool if your docs are in a separate repository, in a CMS, or in a format you cannot convert to .mdx, because the embeddings script reads the local pages tree and nothing else.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 126 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The specific gap nextjs-openai-doc-search fills

Most documentation sites ship a keyword search. A reader who types "how do I rotate a key" gets back every page containing the word "key". The starter targets the other case: a reader asks a question in natural language and gets an answer assembled from the most relevant passages of the docs. The README frames the whole thing as taking "all the `.mdx` files in the `pages` directory" and using them as "custom context within OpenAI Text Completion prompts".

The intended user is a team that already runs a Next.js documentation site and keeps its content in the repository. If your docs are already `.mdx` files under `pages`, the distance between your current state and a working search box is one environment variable and a deploy. If your docs live in a CMS, a wiki, or a separate repository, the starter gives you nothing until you solve content ingestion yourself, and it does not attempt to solve it.

The scope is deliberately narrow. There is no crawler, no sitemap reader, no plugin system for other content sources. That narrowness is the point: it is a reference implementation you fork, not a platform you configure.

Build-time embeddings and the checksum table

The pipeline splits cleanly in two. Steps one and two run at build time, when Vercel builds the Next.js app. The `generate-embeddings` script chunks each `.mdx` page into sections, calls the OpenAI embeddings API for each section, receives a `vector(1536)` back, and writes the vector into Postgres via pgvector.

The part worth noticing is the checksum. The README states that the script "generates a checksum for each of your `.mdx` files and stores this in another database table to make sure the embeddings are only regenerated when the file has changed". Without that, every deploy would re-embed the entire documentation set and bill you for it. With it, an unchanged file costs nothing.

That design has a consequence people miss. The checksum is per file, not per section. Editing one paragraph in a long page re-embeds every section of that page. For a documentation set with a handful of large reference pages, the saving is smaller than it looks.

The chunking is also the least documented part of the repository. The README says pages are chunked "into sections" and does not describe the boundaries. If your `.mdx` files rely on unusual heading structures, the sections that reach the embedding API may not match the sections a reader would recognise.

What happens at runtime, from query to streamed answer

Runtime is four moving parts. The `SearchDialog` component in `components/` is the client. When a user submits a question, it posts to the `vector-search` edge function in `pages/api/vector-search.ts`.

The edge function then does two things in sequence. First it asks OpenAI to embed the query, getting back another `vector(1536)`. It runs that vector against pgvector for similarity, and pgvector returns the relevant documentation content. Second, it builds a completion request whose prompt is the user's query plus that retrieved content, sends it to OpenAI, and streams the response back to the browser as `text/event-stream`.

Two properties follow from this. The answer is only as good as the retrieval step, since the model sees nothing except what pgvector handed it. And the client never talks to the database directly for search, so the retrieval logic lives in one place you can read.

The README marks both the similarity search and the prompt injection as `critical` in its sequence diagram, which is a fair description: neither has a fallback path. If the embeddings call fails, the request fails.

Installing nextjs-openai-doc-search and running a first query

Local development needs Docker running, because Supabase runs locally in containers. Start by copying the environment template. The `.env.example` file documents each variable and where its value comes from.

bash
cp .env.example .env

Open `.env` and set `OPENAI_KEY`. The comments in the file point to the OpenAI API keys page. The two Supabase variables stay empty until Supabase is running.

bash
supabase start
supabase status

The first command applies the migrations in `supabase/migrations/`, which is where the pgvector extension is configured. The second prints the credentials. Copy the values into `NEXT_PUBLIC_SUPABASE_ANON_KEY` and `SUPABASE_SERVICE_ROLE_KEY` in `.env`.

bash
pnpm dev

The app is then served on localhost:3000. To point it at your own content, rename or convert your markdown files to `.mdx` inside `pages`, regenerate embeddings, and restart.

bash
pnpm run embeddings
pnpm dev

The README notes that Supabase must be running when you run `pnpm run embeddings`, and suggests `supabase status` to check. There is also a `pnpm run embeddings:refresh` script, which passes a `--refresh` flag to the same script; the README does not describe what that flag does.

Deploying through the Vercel integration

The README's primary path is not local development at all. It is the "Deploy with Vercel" button, which clones the repository and provisions a Supabase project through the Vercel integration. According to the README, that integration "will automatically set the required environment variables and configure your Database Schema". The only manual step is setting `OPENAI_KEY`.

This is the fastest route and also the one that hides the most. The database schema arrives pre-configured, so you never see the migration run. If you later want to change how content is chunked or add a column, you are working against a schema you did not create.

Because embeddings run during the build, `pnpm run build` is defined as `pnpm run embeddings && next build`. That means every production deploy touches the OpenAI API and the database before the Next.js build even starts. A deploy can fail at the embeddings stage, and when it does, the failure is an API or database error rather than a build error.

Where nextjs-openai-doc-search breaks down

The content source is the hard boundary. The script reads `.mdx` files from the `pages` directory of this same Next.js app. Documentation hosted anywhere else requires you to write the ingestion yourself, and the repository offers no extension point for it.

Build-time embedding has a second cost that is easy to underestimate. Every content change requires a rebuild before the change is searchable. There is no incremental reindex at runtime. For a documentation site that publishes several times a day, that coupling between content edits and deploys is a real constraint.

The runtime path has no caching layer described in the README. Every question produces an embeddings call plus a completion call. There is nothing in the documented architecture that deduplicates repeated questions, so a popular question asked by many readers is paid for many times.

Finally, the README does not document what happens when retrieval returns nothing useful. The model still receives the prompt and still answers. There is no described threshold below which the system declines to answer, which is the failure mode that matters most for a documentation search: a confident answer that no page supports.

How it compares to a hosted docs search product

The obvious alternative is a hosted search service that offers semantic or AI answers as a managed feature. The difference is where the data lives and who owns the pipeline. With a hosted product you upload content or point it at a crawler, and the vendor owns chunking, embedding, and retrieval. You get an answer box without operating a database.

With nextjs-openai-doc-search, the vectors sit in your own Postgres instance under pgvector, the retrieval query runs in your edge function, and the prompt is assembled in code you can read and change. That matters if you need to inspect why a particular answer was produced, or if you want to tune retrieval without waiting on a vendor's roadmap.

The trade is operational surface. A hosted service does not ask you to run Docker locally, manage Supabase migrations, or keep an OpenAI key in a build environment. This starter asks for all three. Choosing it means accepting that you now operate a retrieval pipeline, not just a search box.

Maintenance, licence, and what a fork costs you

The repository is not archived, and its last push was on 2026-05-12. The dependencies in `package.json` are pinned to specific versions rather than ranges for the framework itself: Next.js is fixed at 13.2.4, React at 18.2.0, and the OpenAI SDK at `^3.3.0` alongside `openai-edge` at `^1.1.0`. Two OpenAI client libraries in one project is a sign of a template that grew rather than a library with a stable interface.

Because this is a starter, upgrading is not a supported operation. You fork it, and from that point the upgrade path is whatever you build. There are no releases retrieved for the repository, so there is no versioned artefact to track.

The licence is Apache-2.0, which permits commercial use, modification, and redistribution, and includes an explicit patent grant. It also requires that you preserve the licence and notice files and state significant changes. This is a description of the licence text, not legal advice; if you are embedding the code in a product with unusual compliance requirements, have someone qualified read the LICENSE file.

One practical cost deserves naming. Because embeddings are generated at build time against a paid API, your CI or hosting provider needs a valid `OPENAI_KEY` in its build environment. That key is then present during every build, not just at runtime.

Editorial conclusion

Adopt this starter if your documentation already lives as .mdx files inside a Next.js pages directory and you want a working retrieval-augmented search box rather than a general-purpose RAG framework. It is the wrong tool if your docs are in a separate repository, in a CMS, or in a format you cannot convert to .mdx, because the embeddings script reads the local pages tree and nothing else. Before committing, verify that your content survives the chunking step, that the checksum table is actually populated in your Supabase project, and that your OpenAI spending limits match how often the build regenerates embeddings.

Frequently asked questions

What does nextjs-openai-doc-search actually do?

It is a Next.js starter that reads the `.mdx` files in your `pages` directory, embeds them into Postgres with pgvector at build time, and answers natural language questions by retrieving relevant sections and passing them to an OpenAI completion prompt. The response streams back to the browser.

Do I need a Supabase account to run nextjs-openai-doc-search?

You need a Postgres instance with pgvector. The README's local path runs Supabase in Docker via `supabase start`, and the deploy path provisions a Supabase project through the Vercel integration. Both routes assume Supabase.

Why does nextjs-openai-doc-search regenerate embeddings on every build?

It does not regenerate all of them. The script stores a checksum per `.mdx` file and only re-embeds files whose contents changed. Note that the checksum is per file, so editing one paragraph in a long page re-embeds every section of that page.

Can nextjs-openai-doc-search use markdown files instead of mdx?

The README says documentation needs to be in `.mdx` format by default, and that this can be done by renaming existing or compatible markdown `.md` files. After adding content you run `pnpm run embeddings` and then `pnpm dev` again.

Which files handle the search request in nextjs-openai-doc-search?

The client side is the `SearchDialog` component in `components/`, and the server side is the `vector-search` edge function at `pages/api/vector-search.ts`. The edge function embeds the query, runs the pgvector similarity search, and builds the completion prompt.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. Project website
  4. README
  5. supabase-community/nextjs-openai-doc-search on GitHub
Community notes

Community notes