# Autocomplete: Loading, empty and error states

> What the slotsmith Autocomplete shows instead of options: the loading, empty, minimum-characters and error rows, and how to change them.

When the Autocomplete has no options to show, its list shows one status row instead: loading, empty, an error with a retry button, or a prompt to type more. Each comes from a prop you pass, and each row is a slot.

```tsx
import { useId, useState, type ReactNode } from "react";
import { Autocomplete, fold } from "slotsmith/autocomplete";
import { countries, type Country } from "../shared/options";

const byCode = { getOptionValue: (entry: Country) => entry.code, getOptionLabel: (entry: Country) => entry.name };

/** A visible label, and the id that names the picker after it. */
function Field({ label, children }: { label: string; children: (labelId: string) => ReactNode }) {
  const id = useId();
  return (
    <div>
      <p id={id}>{label}</p>
      {children(id)}
    </div>
  );
}

export default function States() {
  const [failed, setFailed] = useState(true);
  const [results, setResults] = useState<Country[]>([]);

  return (
    <div style={{ display: "grid", gap: "1rem", gridTemplateColumns: "repeat(2, minmax(0, 1fr))" }}>
      <Field label="Loading">
        {/* A first page is in flight and there is nothing to show yet. */}
        {(id) => <Autocomplete options={[]} loading aria-labelledby={id} />}
      </Field>
      <Field label="Empty">
        {(id) => <Autocomplete options={[]} labels={{ empty: "No countries match" }} aria-labelledby={id} />}
      </Field>
      <Field label="Error">
        {/* The error replaces the list; onRetry adds the retry button. */}
        {(id) => (
          <Autocomplete<Country>
            {...byCode}
            options={failed ? [] : countries}
            error={failed ? "503 Service Unavailable" : undefined}
            onRetry={() => setFailed(false)}
            aria-labelledby={id}
          />
        )}
      </Field>
      <Field label="Min. characters">
        {/* Nothing is searched until three characters are typed. */}
        {(id) => (
          <Autocomplete<Country>
            {...byCode}
            options={results}
            filter={false}
            minChars={3}
            onSearchChange={(query) => {
              const text = query.trim();
              setResults(text.length >= 3 ? countries.filter((entry) => fold(entry.name).includes(fold(text))) : []);
            }}
            aria-labelledby={id}
          />
        )}
      </Field>
    </div>
  );
}
```

Open each picker to see its row. Retry in the error picker clears the error and shows the options.

## Which row shows

One row shows at a time, in this order:

1. **Error**, while [`error`](https://slotsmith.dev/components/autocomplete/api/#prop-error) is set. It replaces the list entirely, so a failed request never reads as "No results". With [`onRetry`](https://slotsmith.dev/components/autocomplete/api/#prop-onRetry) it has a retry button named by the [`retry`](https://slotsmith.dev/components/autocomplete/api/#label-retry) label.
2. **Loading**, while [`loading`](https://slotsmith.dev/components/autocomplete/api/#prop-loading) is true and there are no options to show. With options on screen, the list keeps them and sets `aria-busy="true"` instead. The trigger shows a spinner whenever `loading` is true.
3. **Minimum characters**, while fewer than [`minChars`](https://slotsmith.dev/components/autocomplete/api/#prop-minChars) characters are typed and there are no options, from the [`minChars`](https://slotsmith.dev/components/autocomplete/api/#label-minChars) label.
4. **Empty**, when the search matches nothing, from the [`empty`](https://slotsmith.dev/components/autocomplete/api/#label-empty) label.

The live region stays silent while a status row shows; it reads the result count again once options are back.

## Changing the rows

- The text: pass [`labels`](https://slotsmith.dev/components/autocomplete/api/#prop-labels), as the demo does with `empty: "No countries match"`.
- The row itself: replace the [`Empty`](https://slotsmith.dev/components/autocomplete/api/#slot-Empty), [`Loading`](https://slotsmith.dev/components/autocomplete/api/#slot-Loading) or [`Error`](https://slotsmith.dev/components/autocomplete/api/#slot-Error) slot. `Empty` receives the query and the status, so one component can word "no results" and "type more" differently.
- In your own layout, [`Autocomplete.StatusRows`](https://slotsmith.dev/components/autocomplete/guides/compound-parts/) renders the right row for you.

The [creatable](https://slotsmith.dev/components/autocomplete/guides/creatable/) guide shows the row that replaces "No results" when new options can be made.
