Agent-readable docs index: /llms.txt. Download /docs.zip to grep all markdown files locally.

Spiceflow

Spiceflow is a type-safe API and React Server Components framework for TypeScript. Holocron uses it as its web framework under the hood.
When you add holocron() to your Vite config, the plugin automatically adds the Spiceflow Vite plugin, React, and Tailwind CSS. You don't configure any of these separately. Holocron creates a Spiceflow app internally that registers routes for every page in your docs.json navigation.
vite.config.ts │ ▼ holocron() plugin │ ├── spiceflow() vite plugin (auto-added) ├── @vitejs/plugin-react (auto-added) ├── @tailwindcss/vite (auto-added) │ └── creates Spiceflow app internally │ ├── page routes from docs.json ├── layout with sidebar, navbar, search ├── MDX rendering pipeline └── static assets (CSS, icons, fonts)
For most documentation sites, you never interact with Spiceflow directly. The holocron() plugin handles everything.

When you need Spiceflow

Sometimes a docs site needs more than static pages. You might want to:
  • Add API routes for webhooks, health checks, or data endpoints
  • Add middleware for auth, logging, or custom headers on every request
  • Add redirects that can't be expressed in docs.json
  • Serve custom pages like a dashboard or admin panel alongside docs
  • Mount holocron under a sub-path of a larger application
In these cases, you create a custom entry file that gives you a full Spiceflow app. Holocron becomes a child app you mount with .use().

Custom entry setup

Pass entry to the holocron plugin:
// vite.config.ts import { defineConfig } from 'vite' import { holocron } from '@holocron.so/vite' export default defineConfig({ plugins: [ holocron({ entry: './src/server.tsx' }), ], })
Then create your server file. Import the holocron app and mount it last so your own routes take priority:
// src/server.tsx import { Spiceflow } from 'spiceflow' import { app as holocronApp } from '@holocron.so/vite/app' export const app = new Spiceflow() // API routes .get('/api/health', () => ({ status: 'ok' })) .post('/api/webhook', async ({ request }) => { const body = await request.json() // process webhook... return { received: true } }) // mount holocron last — it handles all docs pages .use(holocronApp)
Request ──► your middleware ──► your routes ──► holocronApp │ docs pages, search, sidebar, MDX rendering
Your routes are checked first. If none match, the request falls through to holocron which serves the docs pages, search API, and static assets.

Middleware

Middleware registered before .use(holocronApp) runs on every request, including docs pages. This is useful for auth, analytics, or headers:
export const app = new Spiceflow() .use(async ({ request }, next) => { console.log(request.method, new URL(request.url).pathname) const res = await next() if (res) res.headers.set('x-docs-version', '2.0') return res }) .use(holocronApp)
For auth-gated docs, check the session before holocron handles the request:
export const app = new Spiceflow() .use(async ({ request }, next) => { const url = new URL(request.url) // public pages if (url.pathname === '/' || url.pathname.startsWith('/api/')) { return next() } // check auth for all other pages const session = await getSession(request) if (!session) { return Response.redirect(new URL('/login', url.origin).href) } return next() }) .use(holocronApp)

Serving extra files

A common pattern is serving an llms.txt file for AI agents or adding redirects that need custom logic:
import readmeRaw from '../README.md?raw' export const app = new Spiceflow() .get('/llms.txt', () => { return new Response(readmeRaw, { headers: { 'Content-Type': 'text/plain' }, }) }) .get('/gh', ({ request }) => { return Response.redirect('https://github.com/example/repo', 302) }) .use(holocronApp)

Cloudflare Workers

When deploying to Cloudflare Workers, your entry file needs a default export with a fetch handler:
import { Spiceflow } from 'spiceflow' import { app as holocronApp } from '@holocron.so/vite/app' export const app = new Spiceflow() .get('/api/hello', () => ({ hello: 'world' })) .use(holocronApp) export default { async fetch(request: Request): Promise<Response> { return app.handle(request) }, }
Add the Cloudflare Vite plugin after holocron in your config:
import { cloudflare } from '@cloudflare/vite-plugin' import { holocron } from '@holocron.so/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ holocron({ entry: './src/server.tsx' }), cloudflare({ viteEnvironment: { name: 'rsc', childEnvironments: ['ssr'], }, }), ], })
See Cloudflare Workers deployment for wrangler.jsonc setup and build commands.

Real-world examples

  • The holocron.so website uses a custom entry with auth routes (better-auth), an AI gateway proxy, and device authorization flow alongside the docs.
  • The spiceflow website uses a custom entry to serve /llms.txt and /gh redirect alongside holocron docs, deployed to Cloudflare Workers.