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
sourceand a real accessiblelabel. - 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/queryper settled input, not one per keystroke. - Confirm the response items render
displayValue, and that selecting one produces the value you expect inonSelect. - 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_keyrather 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, storevalue. They differ:displayValueis 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.