# Integration Tiers | React AI Autocomplete Docs

> For the complete documentation index, see [llms.txt](https://ai-autocomplete.com/llms.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`.

Integration Tiers guide for integrating the AI Autocomplete SDK with React. Code examples, setup, and API reference.

Source: https://ai-autocomplete.com/docs/react/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**

```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)}
    />
  );
}
```

### 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**

```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**

```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.

**HookWithDropdown.tsx**

```tsx
import {
  useAIAutocomplete,
  AIAutocompleteDropdown,
} from "@magicx-eng/ai-autocomplete-react";

export function HookWithDropdown() {
  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>
  );
}
```

The hook returns inputProps and dropdownProps — spread them onto your <textarea> and <AIAutocompleteDropdown /> respectively. Don't override their handlers unless you mean to. The dropdown is absolutely positioned, so render it inside a position: relative container (as in the example above).

### 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 →](https://github.com/magicx-ai/ai-autocomplete-tiptap-example)

Always call reset() after submit

Tier 1 auto-resets after its built-in submit. In Tier 2 and Tier 3 you own the submit flow — don't pass onSubmit to useAIAutocomplete(). Wire up submit however you like (a form onSubmit, a button onClick, a custom Enter handler) and call the reset() returned by the hook at the end of that handler. It clears the input and rotates the per-session session_id so the server's history stays accurate; skipping it degrades suggestion quality on subsequent submissions.

## 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**

```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>
  );
}
```

You still feed the input the same way (inputProps for a <textarea>, or the actions above for a custom editor). Tier 3 only changes who renders the suggestions.
