Skip to content
AutoSuggesAutoSugges home
Start free
Menu
Appearance
Appearance: System.

Add AutoSugges autocomplete to a plain HTML page

Mount an accessible autocomplete on an ordinary HTML page from a data attribute, with no framework and no hand-written combobox markup.

How do I add autocomplete to a plain HTML page?

Add data-autosugges-key and data-autosugges-base-url attributes to any element, then load the browser build from /sdk/v1/autosugges.js. Loading the script mounts an accessible autocomplete inside a shadow root, so host page styles cannot interfere. No bundler, framework or hand-written combobox markup is required.

What you will have

A working autocomplete on a plain HTML page, rendered inside a shadow root so it cannot collide with the host page’s styles, with no combobox markup written by hand.

Before you start

  • An AutoSugges list that has been published at least once. An unpublished list has nothing to serve yet and returns list_not_published.
  • The publishable key issued for the application that will query it.
  • The runtime base URL for the environment you are targeting. AutoSugges does not have one fixed public hostname baked into the SDK — the dashboard's integration panel shows the origin for your environment.
  • The AutoSugges web app's own origin, to load /sdk/v1/autosugges.js from — the dashboard's integration panel shows it. No bundler required.

Values you supply

Each value this integration needs, where it comes from, and whether it is public
ValuePlaceholderWhere it comes from
baseUrlRequired · public by designYOUR_RUNTIME_BASE_URLThe AutoSugges dashboard's integration panel, for the environment you are deploying to. The origin of the AutoSugges runtime — scheme and host, no trailing slash and no path. The SDK appends /v1/... itself.
publishableKeyRequired · public by designYOUR_PUBLISHABLE_KEYThe AutoSugges dashboard, under the application that will make the queries. Identifies the consumer, the application, the subscription, the canonical list and the query policy in a single server-side lookup. The client never supplies a list id, tenant id or version hash — if a generated integration is passing one of those, it is wrong.
accessTokenOptional · never in client codeYOUR_SERVER_MINTED_ACCESS_TOKENYour own backend, which mints it from your AutoSugges credentials. Only for a list whose policy requires a token. Short-lived, sent as authorization: Bearer <token>, and never minted, stored or hardcoded in client code.

Steps

  1. Mark up one element. Give any element data-autosugges-key and data-autosugges-base-url. Optional: data-autosugges-label, -placeholder, -locale, -theme.
  2. Load the browser build. Add <script src=".../sdk/v1/autosugges.js"></script>. Loading it is what triggers the one-time scan that mounts every [data-autosugges-key] element — no import, no bundler.
  3. Or mount explicitly. For markup injected after that scan, or when you need the selection event, call mountAutocomplete(target, { source, label, onSelect, onError }) with a client you created yourself.
  4. Style it from outside. The widget lives in a shadow root. Theme it with the documented CSS custom properties from an ancestor; host page rules cannot reach inside it, and its rules cannot leak out.

Install

No install step. Load the prebuilt browser build with a <script> tag: <script src="YOUR_AUTOSUGGES_WEB_ORIGIN/sdk/v1/autosugges.js"></script>, or from the jsDelivr CDN: https://cdn.jsdelivr.net/npm/@autosugges/client/dist/browser/autosugges.js. For a pinned build use the immutable /sdk/v1/autosugges.<hash>.js; /sdk/v1/autosugges.js always resolves to the current v1 build.

Code

index.html

<!-- Load the prebuilt browser build once, near the end of <body>. It is an
     IIFE — no type="module", no bundler — that also exposes window.AutoSuggesSDK
     for Option B below. -->
<script src="YOUR_AUTOSUGGES_WEB_ORIGIN/sdk/v1/autosugges.js"></script>

<!-- Option A — no JavaScript to write. Loading the script above scans for
     this attribute and mounts an accessible combobox (input + listbox + live
     region) inside its own shadow root, so page styles cannot leak in. -->
<div
  data-autosugges-key="YOUR_PUBLISHABLE_KEY"
  data-autosugges-base-url="YOUR_RUNTIME_BASE_URL"
  data-autosugges-label="City"
  data-autosugges-placeholder="Search for a city"
></div>

<!-- Option B — explicit control, for markup injected after that scan ran, or
     when you need the selection event. window.AutoSuggesSDK is the same
     module the script tag above already loaded; there is nothing further to
     import. -->
<div id="city-mount"></div>
<p id="city-error" role="alert"></p>

<script>
  const { createAutosuggesClient, mountAutocomplete } = window.AutoSuggesSDK;

  const source = createAutosuggesClient({
    baseUrl: 'YOUR_RUNTIME_BASE_URL',
    publishableKey: 'YOUR_PUBLISHABLE_KEY',
  });
  void source.bootstrap().catch(() => {});

  const mounted = mountAutocomplete(document.getElementById('city-mount'), {
    source,
    label: 'City',
    onSelect(item, { ancestors }) {
      // Wire item.value / item.displayValue / ancestors into your form.
    },
    onError(error) {
      // Never invent a message: error.message is already the SDK's safe,
      // ErrorCode-mapped text. Show it, or branch on error.code.
      document.getElementById('city-error').textContent = error.message;
    },
  });
  // mounted.destroy() removes every node and listener it added.
</script>

Security

  • The data attributes are visible in page source. That is fine: both values are public by design. Never add an attribute carrying a secret or an access token.

Check that it works

  1. Load the page and confirm an input appears inside the target element and that the element gained a data-autosugges-mounted attribute.
  2. Type below and then above the minimum query length and confirm requests start only at the threshold.
  3. Confirm the widget is keyboard-operable and that a screen reader announces the result count.
  4. Remove data-autosugges-base-url and confirm the element renders a safe inline message instead of throwing into the page.
  5. If you mounted explicitly, call mounted.destroy() and confirm every node and listener is removed.

An accessible input and listbox appear where the target element was, isolated in a shadow root, returning suggestions from the published list.

Try it live

Paste a publishable key from one of your published lists to run a real query against this environment’s runtime — the same @autosugges/client the code above uses.

A published list's publishable key — a public identifier, safe to paste here.

Paste a publishable key to try a live query.

Notes

  • Never hardcode a minimum query length or a debounce interval. Both come from client.policy, which the SDK fills from the bootstrap response and updates in place. The constants the SDK falls back to before the first successful bootstrap are documented defaults, not the list’s real policy.
  • Render displayValue, store value. They differ: displayValue is resolved for the requested locale at compile time.
  • A selection report is optional. Omitting it costs ranking quality over time and nothing else; it is never required for a query to work.
  • The binding never throws into the host page. A configuration or query failure degrades to the widget’s own error state, so check the console and the network panel rather than expecting an exception.

Recipes for specific datasets

The same integration, specialised to the datasets whose behaviour changes the code.

Frequently asked questions

What is the minimum number of characters before a query runs?
Two by default. A list owner can change the minimum, but it can never be 1. The client reads the real value from bootstrap and sends no request below it, so do not hardcode it.
How long does the client wait after typing before it queries?
150 milliseconds by default. The value is delivered through bootstrap and a list owner can raise it, so the client uses the policy value rather than a constant.
Where does the publishable key go?
In the key query parameter of the request, for example /v1/query?key=YOUR_PUBLISHABLE_KEY, never in a header. The key is public by design and safe in client code. Secret keys never belong in a browser or mobile bundle, and a proxy to hide the publishable key is unnecessary.
What happens when my query quota runs out?
The runtime returns a structured quota_exceeded response that carries no items and no placeholder suggestions, so exhaustion is never mistaken for no results. Render it from the typed error code and prompt an upgrade.
Can my page styles break the widget?
No. The widget lives in a shadow root, so host page rules cannot reach inside it and its rules cannot leak out. Theme it with the documented CSS custom properties set on an ancestor element.
. Loading it is what triggers the one-time scan that mounts every [data-autosugges-key] element — no import, no bundler."},{"@type":"HowToStep","name":"Or mount explicitly","text":"For markup injected after that scan, or when you need the selection event, call mountAutocomplete(target, { source, label, onSelect, onError }) with a client you created yourself."},{"@type":"HowToStep","name":"Style it from outside","text":"The widget lives in a shadow root. Theme it with the documented CSS custom properties from an ancestor; host page rules cannot reach inside it, and its rules cannot leak out."}]}