For AI agents: a documentation index is available at /llms.txt, and the full corpus at /llms-full.txt. A markdown version of any page on this site is available by appending .md to its URL path — the homepage is at /index.md.

Field Injection

Serve a field's options from your own data (an airport search, a customer list, live inventory) while AI Autocomplete keeps guiding the rest of the query.

How it works

You mark a field as injected in Edit Logic, then hand the SDK a function for it. Whenever that field becomes the active pill, the SDK asks your function for options instead of using its own. For a travel booking flow with a location field, one round looks like this:

Sequence of a runtime-injected location field: the user types, the SDK asks your app for options, and the field completes on a pick.Your userAI Autocomplete SDKYour apptypes “Flights to”next field: [location] (injected)location("", signal)the default list, before any typingtypes “SF”location("SF", signal)["SFO", "Oakland"]shown in the dropdown as-is[]no match: AI Autocomplete handles the phrasepicks “Oakland”field complete, guiding resumes
One injected field, start to finish. Solid arrows to and from your app are the calls your optionOverrides function receives and answers; the dashed arrow is the empty-answer path.
  1. The user types, and AI Autocomplete suggests the next field to fill. Here, that is location.
  2. Because location is injected, the SDK calls your function with what the user has typed for it so far ("" at first, then "SF"). Your function can call your own search endpoint.
  3. Whatever you return is shown in the dropdown as-is, so a fuzzy match your endpoint found (Oakland for "SF") is never hidden.
  4. The user picks an option (or types one out in full) and the field completes. AI Autocomplete suggests the next field, and its own options take over again.

Step 1: Mark the field as injected

AI Autocomplete needs to know which fields you own, so it can suggest them at the right moment and leave their options to you.

  1. Open Edit Logic and answer Yes to the custom data fields question.
  2. Add a field and give it the name you will use in code, for example location.
  3. Pick "I will inject the values at runtime" as its type.
  4. Save. Your product regenerates with the new field, and the SDK surfaces it as a pill whenever the query calls for it.

Step 2: Connect your data

Pass optionOverrides with one entry per injected field. Each entry is a function that:

  • Receives the text the user has typed for that field, plus an AbortSignal.
  • Returns the options to show, or a promise of them, so it can call your own endpoint.
  • Keeps the dropdown in its loading state until the promise settles.
fieldInjection.tsx
import { AIAutocomplete, type OptionOverrides } from "@magicx-eng/ai-autocomplete-react";
// "location" added as a runtime-injected custom field in Edit Logic.
// The key must match the field name exactly as typed there.
const overrides: OptionOverrides = {
location: async (query, signal) => {
// Your own search: "SF" can come back as SFO and nearby Oakland.
const res = await fetch(`/api/airports?q=${encodeURIComponent(query)}`, { signal });
const airports: { code: string; city: string }[] = await res.json();
return airports.map((a) => ({
text: `${a.city} (${a.code})`,
is_tappable: true,
kind: null,
metadata: { code: a.code }, // read it back on submit
}));
},
};
// Inline is fine: the functions are read live on each call, so a fresh
// object per render is not a swap. Only the set of keys matters.
<AIAutocomplete optionOverrides={overrides} onSubmit={handleSubmit} />;

Fixed and computed lists

Not every injected field needs a request. Return an array directly for a list you already have, or compute options from what the user typed.

fixedLists.tsx
const overrides: OptionOverrides = {
cabin: () => [
{ text: "Economy", is_tappable: true, kind: null },
{ text: "Premium economy", is_tappable: true, kind: null },
{ text: "Business", is_tappable: true, kind: null },
],
travelers: (query) => {
const n = query.replace(/\D/g, "");
return [{ text: n ? `${n} travelers` : "2 travelers", is_tappable: true, kind: null }];
},
};

What the SDK does with your answer

  • Your function is called the moment the field becomes active, with whatever the user has already typed for it (usually "", the request for the default list). It is called again with each new phrase after the SDK's typing debounce.
  • The list you return is shown as-is. The SDK does not filter it again by the phrase it was produced for, so a match your endpoint found (Oakland for "SF") stays visible.
  • Between two calls, the SDK filters your last answer locally by what the user types, so the dropdown keeps up on every keystroke.
  • While an answer is pending, the dropdown shows its loading state and isLoading is true, the same flag it raises while waiting on AI Autocomplete.
  • Picking an option, or typing an option's text out in full, completes the field. AI Autocomplete then suggests the next field, and its own options take over until another injected field comes up.

When nothing matches

Return an empty list for something the user typed and the SDK stops waiting on you for that phrase:

  • The typed text is handed to AI Autocomplete, which treats it like any field with no matching options, so the user is never stuck.
  • You are asked again as soon as the phrase changes.
  • An empty list for "" (the default-list request) leaves the field on screen with no options until the user types something.

Cancellation and errors

  • Honour the signal. It is aborted when a newer phrase supersedes the call, when the field stops being active, and when the widget is torn down. Pass it to fetch so a stale request is dropped instead of racing the new one.
  • A thrown error or rejected promise is contained: it is logged once and treated as an empty answer. It never surfaces as a fetch error or breaks the widget.

Option shape

Each entry you return is a suggestion option, the same shape AI Autocomplete's own options use.

PropTypeDescription
textRequiredstringLabel shown in the dropdown, and inserted into the query when picked.
is_tappableRequiredbooleantrue for an option the user can pick. false renders it as a non-interactive hint (hidden entirely when showNonTappableOptions is false).
kindRequiredTaskKind | nullSet to null for a field value. Reserved for options that trigger a task rather than fill the field.
iconstringOptional emoji or short glyph shown before the label.
tagstringOptional small tag rendered beside the label: a category, a distance, a price.
metadataRecord<string, unknown>Anything you want to carry with the option, such as an airport code behind a display name. It is kept on the completed parameter, so it is available to you on submit.