Vite integration
There are two indexing strategies.
Let Vite generate the index
export default defineConfig({ plugins: [seekite()] })After a production build, the plugin indexes generated HTML and configured
sources into <outDir>/search. During development it rebuilds into
<publicDir>/search and serves those assets without caching. Use this for
runtime vectors or when the Vite build has access to the corpus.
Generate in CI and publish through Vite
// search.config.ts
export default defineSearch({
output: "public/search",
corpora: { /* developer-owned sources */ },
})
// vite.config.ts
export default defineConfig({
plugins: [seekite({ indexing: "external" })],
})Run these commands in order:
seekite build
vite buildExternal mode does not retrieve or index the corpus inside Vite. It verifies
public/search/manifest.json, then Vite's normal public-directory handling
copies and links the whole search asset tree into the bundle. This keeps API
credentials and expensive static embedding work in the CI indexing step.
Persist .seekite/cache between CI builds so unchanged chunks do not run
through the embedding provider again. For GitHub Actions:
- uses: actions/cache@v4
with:
path: .seekite/cache
key: seekite-embeddings-${{ runner.os }}
restore-keys: seekite-embeddings-
- run: pnpm exec seekite build --output public/searchThe key intentionally stays stable; Seekite's internal content hashes handle invalidation. See Incremental embedding builds for pruning and Netlify/Cloudflare cache-mount guidance.
Set assetsDir on the plugin when using a name other than search, and use the
same name in output. Initialize the client with createSearch({ key: "search" });
Seekite resolves the key below Vite's configured base path without exposing
import.meta.env.BASE_URL to application code. The key defaults to search.
The first-party embedding package ships bundler-ready Wasm and needs no Ternlight
dependency or optimizeDeps.exclude workaround.
Framework wrappers can configure rendered-HTML extraction through the same plugin. Selector order is significant: the first selector with a match is used.
seekite({
html: {
bodySelector: ["[data-seekite-body]", "main", "article"],
excludeSelectors: ["nav", "aside", ".table-of-contents"],
},
})