Integration Tiers

Three ways to integrate, from drop-in to fully headless — pick the one matching how much UI you want to own.

Tier 1

Full component

Drop-in <AIAutocomplete />. Owns the input, pills, dropdown, and state. Zero JSX you have to write.

Use when you want the complete widget with no setup.

Tier 2

Hook + dropdown

useAIAutocomplete() + <AIAutocompleteDropdown />. You own the input element; we render the dropdown and pills.

Use when you need a custom input but want our dropdown UI.

Tier 3

Headless

useAIAutocomplete() alone — state, actions, and derived data. You render the dropdown too.

Use when you need full control over every piece of the UI.

Tier 1 — drop-in component

The default. AIAutocomplete owns the input element, the dropdown, the pills, and the keyboard handling. Pass apiConfig + onSubmit and you're done.

App.tsx
import { AIAutocomplete } from "@magicx-eng/ai-autocomplete-react";
export function App() {
return (
<AIAutocomplete
apiConfig={{ apiKey: import.meta.env.VITE_MAGICX_PUBLIC_KEY }}
onSubmit={(result) => console.log(result)}
placeholder="Create a"
/>
);
}

Controlled state

Pass value + completedParams + their setters to drive state externally. Useful inside form libraries or wizards. (This also works with the hook in Tier 2 and Tier 3.)

ControlledAutocomplete.tsx
const [text, setText] = useState("");
const [params, setParams] = useState([]);
<AIAutocomplete
value={text}
onChange={setText}
completedParams={params}
onParamsChange={setParams}
onSubmit={(result) => console.log(result)}
/>;

Imperative ref

Forward a ref to call focus(), reset(), or setMode() imperatively from the parent.

ImperativeRef.tsx
import { useRef } from "react";
import {
AIAutocomplete,
type AIAutocompleteHandle,
} from "@magicx-eng/ai-autocomplete-react";
const ref = useRef<AIAutocompleteHandle>(null);
// ref.current?.focus();
// ref.current?.blur();
// ref.current?.reset();
// ref.current?.setMode("dark");
<AIAutocomplete ref={ref} onSubmit={handleSubmit} />;

Tier 2 — hook + dropdown

Drive state with useAIAutocomplete() and render our <AIAutocompleteDropdown /> for the suggestions UI. You own the input element and the surrounding layout; we own the dropdown.

HeadlessAutocomplete.tsx
import {
useAIAutocomplete,
AIAutocompleteDropdown,
} from "@magicx-eng/ai-autocomplete-react";
export function HeadlessAutocomplete() {
const { inputProps, dropdownProps, completedParams, reset } =
useAIAutocomplete({
apiConfig: { apiKey: import.meta.env.VITE_MAGICX_PUBLIC_KEY },
});
const handleSubmit = () => {
sendToBackend(inputProps.value, completedParams);
reset(); // start a new session
};
return (
<div className="my-shell" style={{ position: "relative" }}>
<textarea {...inputProps} />
<AIAutocompleteDropdown {...dropdownProps} mode="auto" />
<button type="button" onClick={handleSubmit}>Submit</button>
</div>
);
}

Color mode

A standalone dropdown styles itself when you pass mode — "light", "dark", or "auto" (which follows the OS theme). It scopes the design tokens to its own root, so no .magicx-aia wrapper is needed. Leave mode unset only when the dropdown sits inside Tier 1 or your own .magicx-aia element, so it inherits that theme instead. (optionsPosition flows through dropdownProps too, so above/below placement works with no extra CSS.)

Custom & rich-text inputs

inputProps is shaped for a <textarea>. For a contentEditable or rich-text editor (Tiptap, ProseMirror, Lexical…) don't spread inputProps — call the actions directly: handleTextChange(text) on every edit, setFocused(bool) on focus/blur, handleKeyDown(event) for Arrow/Enter/Tab/Escape while the dropdown is open, and handleCaretMove(offset) so arrow keys can move into the dropdown.

See the Tiptap integration example →

Tier 3 — headless (bring your own UI)

Skip <AIAutocompleteDropdown /> and render the suggestions UI yourself. dropdownProps carries everything you need — the active suggestion's options, the highlighted index, and the onSelect / onHighlight actions — so you can build a fully custom dropdown while the hook keeps owning state, fetching, filtering, and keyboard logic.

CustomDropdown.tsx
import { useAIAutocomplete } from "@magicx-eng/ai-autocomplete-react";
export function CustomDropdown() {
const { inputProps, dropdownProps } = useAIAutocomplete({
apiConfig: { apiKey: import.meta.env.VITE_MAGICX_PUBLIC_KEY },
});
// dropdownProps carries the data + actions — render it however you like.
const { suggestions, activeIndex, isOpen, onSelect, onHighlight } =
dropdownProps;
const options = suggestions[0]?.options ?? [];
return (
<div style={{ position: "relative" }}>
<textarea {...inputProps} />
{isOpen && (
<ul className="my-dropdown" role="listbox">
{options.map((option, i) => (
<li
key={option.text}
role="option"
aria-selected={i === activeIndex}
onMouseEnter={() => onHighlight(i)}
onMouseDown={(e) => {
e.preventDefault(); // keep focus in the input
onSelect(option);
}}
>
{option.text}
</li>
))}
</ul>
)}
</div>
);
}