Three tiers, from drop-in to headless. Pick the one that matches how much DOM control you want.
Tier 1
Full component
Drop-in. Library creates input + dropdown + pills + keyboard + state.
Use when you want a complete widget with zero setup.
Tier 2
Dropdown only
Library renders dropdown + pills. You own the input element.
Use when you need a custom input (rich text, search bar, etc).
Tier 3
Headless
State + controllers + derived data. You render everything.
Use when you're building a framework wrapper or need full DOM control.
Tier 1 — full component
The library creates and owns the input, dropdown, pills, and all DOM. Just pass a container.
import { AIAutocomplete } from "@magicx-eng/ai-autocomplete-vanilla";const ac = new AIAutocomplete(container, {renderMode: "full", // defaultapiConfig: { apiKey: "pk_v1_your_public_key" },placeholder: "Create a",columns: 2,mode: "auto", // "light" | "dark" | "auto"pillPlacement: "inline", // "inline" | "dropdown" | "hidden"onSubmit: (result) => console.log(result),onError: (err) => console.error(err),});// Imperative methodsac.focus();ac.reset();ac.setMode("dark");ac.update({ animations: false, optionsPosition: "above" });ac.destroy();
Tier 2 — dropdown only
You own the input element. The library renders the dropdown (with pills) inside a container you provide, and you wire your input's events into the library.
import { AIAutocomplete } from "@magicx-eng/ai-autocomplete-vanilla";const input = document.getElementById("my-input") as HTMLTextAreaElement;const dropdownContainer = document.getElementById("my-dropdown")!;const ac = new AIAutocomplete(dropdownContainer, {renderMode: "dropdown",apiConfig: { apiKey: "pk_v1_your_public_key" },onChange: (text) => {if (input.value !== text) input.value = text;},});const handleSubmit = () => {sendToBackend(input.value);ac.reset(); // start a new session};// Wire your input → libraryinput.addEventListener("input", () => ac.handleTextChange(input.value));input.addEventListener("keydown", (e) => ac.handleKeyDown(e));submitButton.addEventListener("click", handleSubmit);
Tier 3 — headless
No DOM at all. You get state, actions, and derived data. You render everything yourself. This is what framework wrappers (React, Vue, Svelte) use under the hood.
import { AIAutocomplete } from "@magicx-eng/ai-autocomplete-vanilla";const ac = new AIAutocomplete(document.createElement("div"), {renderMode: "headless",apiConfig: { apiKey: "pk_v1_your_public_key" },});const handleSubmit = () => {const state = ac.getState();sendToBackend(state.text, state.completedParams);ac.reset(); // start a new session};// Read state synchronouslyconst state = ac.getState();// Subscribe to derived stateconst unsubscribe = ac.subscribe((state) => {renderMyInput(state.text, state.placeholderText);renderMyPills(state.actionableSuggestions);renderMyDropdown(state.filteredOptions,state.activeDropdownIndex,state.isDropdownOpen,);});// Forward user actionsmyInput.addEventListener("input", () => ac.handleTextChange(myInput.value));myInput.addEventListener("keydown", (e) => ac.handleKeyDown(e));myOptionEl.addEventListener("click", () => ac.selectOption(option));myPillEl.addEventListener("click", () => ac.setActivePill(index));mySubmitButton.addEventListener("click", handleSubmit);// Cleanupunsubscribe();ac.destroy();