Loading...

Components

@searchstack/autocomplete is a tiny, framework-agnostic JavaScript module that turns any HTML <input> into an as-you-type search box wired to one of your lists or groups. It handles debouncing, request cancellation, keyboard navigation, mobile full-screen mode and search history out of the box, and lets you render every row however you like with a template function. It ships as both an npm package and a CDN bundle.

It's an optional convenience for web front-ends, not a dependency. Under the hood it just calls the suggest REST endpoint — a plain HTTPS request you can make from any language or platform. If you're not building a web page, skip the module and call suggest directly.

Try it

An interactive preview wired to a live movies list on the public API — start typing a film (try “matrix”, “inception” or “dark”) to see the dropdown, keyboard navigation and custom row template in action:

This box is live: it calls the suggest endpoint on each keystroke using a read-only demo key, and draws each row with a custom template.

Install

From npm:

npm install @searchstack/autocomplete

…or straight from the CDN, no build step:

import { attachList, attachGroup } from 'https://cdn.jsdelivr.net/npm/@searchstack/autocomplete@2/dist/searchstack-autocomplete.js'
Authentication

The component calls the public API on the visitor’s behalf, so every call is authenticated. Pass your API key as the second argument to attachList / attachGroup — it’s required and sent as X-API-Key.

Because the key ships in your page’s JavaScript, use a list-scoped, read-only key — the safest key for a browser. When you create the key, choose A single list (or A single group) as its scope and give it read-only (search-result:read) permission. A list-scoped key can only ever search that one list: if it’s copied out of your page source it grants nothing else — no other list, no account settings, no writes — and tampering with the list name in the URL just returns 403. To authenticate a signed-in user instead, pass their access_token in the options (sent as Authorization: Bearer).

Usage

There are two entry points — attachList for a single list and attachGroup for a group of lists. Both take the same arguments: the id of an <input>, an API key scoped to suggest, your account name, the list or group name, and the version. For a group, pass 'latest' to always track the group’s current membership version (so it never rots when the group is bumped), or a concrete integer to pin a frozen version.

<input id="txt1" />
<script type="module">
  import { attachList, attachGroup } from 'https://cdn.jsdelivr.net/npm/@searchstack/autocomplete@2/dist/searchstack-autocomplete.js'

  // Target a List version:
  await attachList('txt1', 'YOUR_SUGGEST_KEY', 'my-account', 'my-list', 1);

  // ...or a Group, always tracking its current membership version:
  await attachGroup('txt1', 'YOUR_SUGGEST_KEY', 'my-account', 'my-group', 'latest');
</script>
Custom row templates

By default each suggestion renders its name. Pass a template — a function that receives the record and returns an HTML string — to control the row. When targeting a group you can branch on data.list_name to draw a different shape per source list — for example, over a movies-and-actors group:

await attachGroup('movies-actors-txt', 'YOUR_SUGGEST_KEY', 'my-account', 'movies-and-actors', 3, {
  template: (data) => {
    if (data.list_name === 'actors') {
      return `<div style="display:flex;gap:10px">
        <img src="${data.image_url}" style="width:50px" />
        <div><strong>${data.name}</strong><br><small>Actor</small></div>
      </div>`;
    }
    return `<div style="display:flex;gap:10px">
      <img src="${data.poster_url}" style="width:50px" />
      <div><strong>${data.name}</strong> <small>(${data.year})</small><br><small>${data.genre}</small></div>
    </div>`;
  }
});
Options

Every option is passed in the sixth argument to attachList / attachGroup. All are optional.

OptionDefaultWhat it does
api_keyAPI key sent as X-API-Key. Supply this or access_token.
access_tokenJWT sent as Authorization: Bearer. Supply this or api_key.
base_urlpublic APIOverride the API base URL (e.g. a regional or self-hosted endpoint).
delay200Milliseconds to wait after a keypress before calling the API (debounce).
minimum_characters2Don’t call the API until at least this many characters are typed.
suggestion_optionsExtra suggest parameters passed through to the API: size, filter, radius, skip, cache.
templatename onlyFunction (data) => htmlString that renders each row (used on desktop and mobile).
headlessfalseFetch and dispatch suggestions (events / callbacks) without rendering the list.
history_templatetemplateTemplate for previously-selected (history) rows.
allow_multipletrueWhen false, attaching first destroys any previously attached instances.
footer_templateFunction returning HTML appended below the list.
enable_historytrueRemember and prioritise a user’s recent selections.
enable_repositioningfalseLet the list flip to the best fit (right / top / left) when it doesn’t fit below the input.
full_screen_on_mobiletrueOpen the list full-screen on mobile devices.
mobile_max_screen_width500Screen width (px) at or below which mobile behaviour kicks in.
full_lengthtrueMake the list the same width as the input.
list_style / list_item_style / history_item_styleInline CSS injected into the list / each row / each history row (see also CSS variables).
selected, suggested, searchEvent-handler functions (see events below); the same signals fire as DOM events too.
selected_failed, suggested_failedHandlers receiving a status and message when a call fails.
Events

Alongside the handler options, the component dispatches DOM events on document so any part of your page can react:

EventFires when…Payload
searchstack-suggestionssuggestions come backe.data, e.query
searchstack-searchthe user submits a searche.query
searchstack-selecteda suggestion is chosene.data, e.id, e.query
searchstack-suggestions-faileda suggest call failse.status, e.message, e.query
searchstack-selected-failedresolving a selection failse.status, e.message, e.id
document.addEventListener('searchstack-selected', (e) => {
  console.log(e.id, e.data);   // navigate to the chosen record, etc.
});
Theming with CSS variables

Override these custom properties on the input’s container (or :root) to restyle the dropdown without touching the component’s markup:

VariableDefaultControls
--searchstack-list-background-color#fffDropdown background.
--searchstack-list-font-size0.9emSuggestion text size.
--searchstack-list-item-background-hover-colorRow background on hover.
--searchstack-list-item-background-focused-colorRow background on keyboard focus.
--searchstack-list-*-margin-*variesFine margins around the list on each edge (top / right / bottom / left).

The list exposes a full family of per-edge margin variables (--searchstack-list-top-margin-left, …) for pixel-level positioning; set only the ones you need.

Full-screen on mobile

With full_screen_on_mobile: true (the default), the suggestion list opens full-screen on phones for a native-app feel — set it to false to keep the inline dropdown everywhere.

await attachList('txt1', 'YOUR_SUGGEST_KEY', 'my-account', 'my-list', 1, { full_screen_on_mobile: true });
Top