Add AutoSugges autocomplete to a React app
Mount the shipped accessible combobox in a React component — debounce, cancellation, keyboard navigation and ARIA are already wired.
What you will have
A working, accessible autocomplete input in a React application, querying a published AutoSugges list at the edge, with the list’s own query policy honoured and every failure rendered from the typed error code.
Before you start
- An AutoSugges list that has been published at least once. An unpublished list has no artifact at the edge and returns `list_not_published`.
- The publishable key issued for the application that will query it.
- The runtime base URL for the environment you are targeting. AutoSugges does not have one fixed public hostname baked into the SDK — the dashboard's integration panel shows the origin for your environment.
- React 18 or newer.
Values you supply
| Value | Placeholder | Where it comes from |
|---|---|---|
| baseUrlRequired · public by design | YOUR_RUNTIME_BASE_URL | The AutoSugges dashboard's integration panel, for the environment you are deploying to. The origin of the AutoSugges runtime Worker — scheme and host, no trailing slash and no path. The SDK appends `/v1/...` itself. |
| publishableKeyRequired · public by design | YOUR_PUBLISHABLE_KEY | The AutoSugges dashboard, under the application that will make the queries. Identifies the consumer, the application, the subscription, the canonical list and the query policy in a single server-side lookup. The client never supplies a list id, tenant id or version hash — if a generated integration is passing one of those, it is wrong. |
| accessTokenOptional · never in client code | YOUR_SERVER_MINTED_ACCESS_TOKEN | Your own backend, which mints it from your AutoSugges credentials. Only for a list whose policy requires a token. Short-lived, sent as `authorization: Bearer <token>`, and never minted, stored or hardcoded in client code. |
Steps
- Create one client. Call createAutosuggesClient({ baseUrl, publishableKey }) once, at module scope, so it is not recreated on every render.
- Bootstrap once. Call client.bootstrap() at startup. It resolves the list's real minQueryChars and debounceMs into client.policy; the component reads that live on every keystroke.
- Render the component. Import Autocomplete from the @autosugges/client/react subpath — not from the package root, which is deliberately React-free — and pass the client as `source` and a real accessible `label`.
- Handle the selection. onSelect receives (item, { ancestors }). Store item.value, render item.displayValue, and use ancestors to auto-fill parent fields without a second request.
- Handle the failures. onError receives an AutosuggesError. Switch on error.code against the vocabulary below; never render a hand-written message.
Install
Bundle `@autosugges/client` with your existing build tool. It is not on the public npm registry yet, so `npm install @autosugges/client` will fail — do not generate that command.
Code
city-field.tsx
import { useState } from 'react';
import { createAutosuggesClient } from '@autosugges/client';
import { Autocomplete } from '@autosugges/client/react';
// One client per app, created outside the component so it survives re-renders.
const autosugges = createAutosuggesClient({
baseUrl: 'YOUR_RUNTIME_BASE_URL',
publishableKey: 'YOUR_PUBLISHABLE_KEY',
});
// Reads the list's real minChars/debounce (DEC-LIST-003). Fire-and-forget: the
// component works on documented fallbacks until this resolves, and the SDK has
// already logged any failure once at its own boundary.
void autosugges.bootstrap().catch(() => {});
export function CityField() {
const [city, setCity] = useState('');
const [region, setRegion] = useState('');
const [errorCode, setErrorCode] = useState<string | undefined>();
return (
<>
<Autocomplete
source={autosugges}
label="City"
placeholder="Start typing a city"
onSelect={(item, { ancestors }) => {
// item.value is what you store; item.displayValue is what was shown.
// ancestors is the precomputed hierarchy chain, root first — use it to
// auto-fill state/country fields without a second request.
setCity(item.value);
setRegion(ancestors.map((ancestor) => ancestor.displayValue).join(', '));
}}
onError={(error) => {
// error is an AutosuggesError. Switch on error.code — the codes and
// their remediation are listed below. Never invent a message string.
setErrorCode(error.code);
}}
/>
<input type="hidden" name="city" value={city} />
<input type="hidden" name="region" value={region} />
{errorCode !== undefined && <p role="alert">Lookup unavailable ({errorCode}).</p>}
</>
);
}Security
- Both values in the snippet are public by design and belong in client code as written. If a generated integration adds a proxy route, an API secret or a server-side fetch "to protect the key", delete it — there is nothing to protect.
Check that it works
- Type fewer characters than the list’s minQueryChars and confirm no network request is made at all.
- Type a query at or above minQueryChars and confirm exactly one GET to `/v1/query` per settled input, not one per keystroke.
- Confirm the response items render `displayValue`, and that selecting one produces the value you expect in `onSelect`.
- Operate the whole control with the keyboard only: ArrowDown/ArrowUp move the active option, Enter selects, Escape closes.
- Temporarily set the publishable key to a wrong value and confirm the UI shows an error state driven by `invalid_key` rather than an empty list that looks like "no results".
Typing in the field produces a listbox of suggestions from the published list within the configured debounce, the field is fully keyboard-operable and announced by a screen reader, and selecting an option fires onSelect with the canonical item and its ancestors.
Try it live
Paste a publishable key from one of your published lists to run a real query against this environment’s runtime — the same @autosugges/client the code above uses.
A published list's publishable key — a public identifier, safe to paste here (PRD §12).
Paste a publishable key to try a live query.
Notes
- Never hardcode a minimum query length or a debounce interval. Both come from `client.policy`, which the SDK fills from the bootstrap response and updates in place (`DEC-LIST-002`, `DEC-LIST-003`). The constants the SDK falls back to before the first successful bootstrap are documented defaults, not the list’s real policy.
- Render `displayValue`, store `value`. They differ: `displayValue` is resolved for the requested locale at compile time.
- A selection report is optional. Omitting it costs ranking quality over time and nothing else; it is never required for a query to work.
- The component renders its own ARIA combobox pattern. Do not add role="combobox" or aria-* attributes around it — you will produce a nested, conflicting pattern.