Vite plugin
vite-plugin-xdive turns web pages into LLM-ready Markdown while you build, so your app can import them like any other module. Use it to ship docs, pricing or policy pages that are kept in step with a source site, or to generate an llms.txt for your own site. It uses your API key and the scrape endpoint, and caches every page on disk.
Install
npm install -D vite-plugin-xdive
pnpm add -D vite-plugin-xdive
yarn add -D vite-plugin-xdive
bun add -d vite-plugin-xdiveThe plugin needs Vite 5, 6 or 7 and Node 18 or newer. Set your API key in the environment:
export XDIVE_API_KEY=ck_live_...Usage
// vite.config.ts
import { defineConfig } from "vite";
import xdive from "vite-plugin-xdive";
export default defineConfig({
plugins: [
xdive({
pages: [
{ id: "pricing", url: "https://example.com/pricing" },
{ url: "https://example.com/docs/intro" }, // id: example-com-docs-intro
],
llmsTxt: { title: "Example", summary: "Docs and pricing for Example." },
}),
],
});For TypeScript, add once (for example in src/env.d.ts):
/// <reference types="vite-plugin-xdive/client" />React:
import { pages } from "virtual:xdive";
export function Pricing() {
return <pre>{pages.pricing.markdown}</pre>;
}Vue:
<script setup lang="ts">
import { pages } from "virtual:xdive";
const { title, markdown } = pages.pricing;
</script>
<template><h1>{{ title }}</h1><pre>{{ markdown }}</pre></template>Each page is { url, title, markdown, fetchedAt }. pages is also the default export.
Options
| Option | Type | Default | Description |
|---|---|---|---|
pages | { id?, url, formats?, maxAgeMs? }[] | required | Pages to fetch. id defaults to a slug of the URL; markdown is always requested; a cached copy younger than maxAgeMs is used without calling the API. |
apiKey | string | env XDIVE_API_KEY, then CRAWLER_API_KEY | Sent as X-API-Key. Never written to the cache or logs. |
baseUrl | string | env XDIVE_BASE_URL, then CRAWLER_BASE_URL, then the public API | API origin including /api. |
cacheDir | string | node_modules/.cache/xdive | Relative to the Vite root. |
offline | boolean | false | Use only the cache; no API calls, no key needed. A page with no cached copy is an error in build and omitted with a warning in dev. |
failOnError | boolean | unset | See below. |
llmsTxt | { title, summary?, outFile? } | off | Emit llms.txt (llmstxt.org format) and <id>.md for every page into the build output. outFile defaults to llms.txt. |
.env files are read too (XDIVE_API_KEY in .env.local works).
Caching and failures
One JSON file per page is kept in the cache directory.
vite buildalways refreshes every page (except pages whose cached copy is younger thanmaxAgeMs).vite devserves the cached copy instantly and refreshes it in the background; if the content changed, the virtual module is invalidated and the page reloads. A page with no cache is fetched before the first load.- If a fetch fails,
failOnErrordecides:- unset (default): the cached copy is used with a warning. With no cached copy, the build fails and
vite devomits the page with a warning. false: never fails; cached copy with a warning, else the page is omitted with a warning.true: any failed page fails the build or dev start; the cache is not used as a fallback.
- unset (default): the cached copy is used with a warning. With no cached copy, the build fails and
- A missing API key always fails (unless
offline), namingXDIVE_API_KEYandCRAWLER_API_KEY. - Quota errors (402) and other HTTP errors include the API's message and
code. - 429 and 503
SERVER_BUSYare retried honoringRetry-After, reusing oneIdempotency-Keyso a retry never runs or bills a request twice.
CI
Store the key as a secret and expose it as XDIVE_API_KEY:
- run: pnpm build
env:
XDIVE_API_KEY: ${{ secrets.XDIVE_API_KEY }}Cache node_modules/.cache/xdive between runs so a transient API outage does not break the build.
See also
- JavaScript SDK, if you want to call the API from your own code.
- Scrape, the endpoint the plugin calls.
- Errors and limits, for quotas and retries.