suo
Getting started

Quickstart: npx @suo/cli init

Detect your framework and wire the widget into your layout from one command, with an index and a search key you already have.

@suo/cli init detects fumadocs, a Next.js App Router project, or a plain HTML site, writes a small config file, and patches your layout (or your HTML) to render <suo-search>. It does not create an account, an index, or a key for you — have those ready first.

0. Get a host, an index and a search key

init needs three things that already exist: your API host (for example https://api.usesuo.com), an index name, and a suo_search_… key restricted to that index. Create the index and key from the dashboard, or from the command line:

suo crawl https://docs.example.com --index docs --write-key suo_write_YOUR_KEY --host https://api.usesuo.com

crawl creates the index if it does not exist yet (see Quickstart: paste a URL). Then mint a search key for the browser:

suo keys create --name "docs widget" --type search --index docs --host https://api.usesuo.com --admin-key suo_admin_YOUR_KEY

suo_search_… from that response is what init asks for next — it is safe to ship to the browser, unlike the write or admin key you used to create it.

1. Run init

npx @suo/cli init

Run without flags in an interactive terminal, init prompts for whatever it's missing: your host, your search key, your index name. Pass them instead to run non-interactively (in CI, or with --yes):

npx @suo/cli init --host https://api.usesuo.com --search-key suo_search_YOUR_KEY --index docs

SUO_HOST, SUO_SEARCH_KEY and SUO_INDEX work as environment variables too. init writes suo.config.ts next to your project root:

export const suoConfig = {
	host: 'https://api.usesuo.com',
	searchKey: 'suo_search_YOUR_KEY',
	indexName: 'docs',
} as const;

2. What init changes

init detects your project (fumadocs needs source.config.ts plus the fumadocs-core dependency; a Next.js App Router project needs app/layout.tsx; anything else with an index.html falls back to plain HTML) and shows a diff before writing anything:

+ suo.config.ts
+ components/suo-search-widget.tsx
~ app/layout.tsx

For fumadocs and Next, it writes a small SuoSearchWidget component that reads suoConfig and renders <SuoSearch>, then patches your layout to render it. For plain HTML, it patches index.html directly, adding a <script src="https://cdn.usesuo.com/widget@1.js"> before </head> and a <suo-search> tag before </body>, both inside a marked block. Pass --install to also run npm install @suo/react (or your project's package manager) for a fumadocs or Next project.

Re-running init with the same values is a no-op. Re-running it after changing your host, key or index updates suo.config.ts freely, but the layout or HTML patch already carries a marker from the first run, so init stops and asks for --force before touching it again — the same as it does for any file with unrelated content already in the way.

init does not read your content, does not push any records, and does not touch the network at all — it only writes local files. Index your content first, with suo crawl (for a site the crawler can read) or suo push (to write your own records directly — see Push app content).

3. Confirm the widget

Start your dev server and press ⌘K (or, for the plain HTML patch, open the page — the widget renders inline wherever init placed the <suo-search> tag).

4. Keep your index in sync

Re-run suo crawl on a schedule, or suo push from your build or CI, so the index updates as your content changes — init only wires the widget in once and does not need to run again unless you change host, key or index.

Other CLI commands

CommandDoes
suo crawl <url>Crawl a URL into an index, creating the index if it doesn't exist yet
suo push <file>Push records from a local .json/.ndjson file into an existing index
suo keys create|list|revokeCreate, list and revoke API keys
suo evalRun your golden set against a live index — see Measuring relevance

Every command reads --host/SUO_HOST and its key flag/env var independently — none of them read suo.config.ts, which only the widget component init generates reads, at render time in your app.

Next: read Concepts to understand how your content becomes searchable records, or jump straight to Theming the widget.

On this page