HTMLAutocomplete (<datalist>)

Autocomplete Suggestions with <datalist>

The <datalist> element attaches a list of suggested values to a text input. As the visitor types, the browser shows a native dropdown of matching options — but unlike a <select>, the field stays a free-text input. The visitor can pick a suggestion or type something that isn't in the list at all. It's the "did you mean" of forms, built entirely with markup and zero JavaScript.

Basic Usage

Two pieces connect a <datalist> to an input: the input's list attribute, and the datalist's id. They must match exactly.

datalist-basic.html

HTML
<label for="browser">Favorite browser:</label>
<input type="text" id="browser" name="browser" list="browsers" />

<datalist id="browsers">
  <option value="Chrome"></option>
  <option value="Firefox"></option>
  <option value="Safari"></option>
  <option value="Edge"></option>
  <option value="Brave"></option>
</datalist>
How the link works
The list attribute on the <input> points to the id of a <datalist> elsewhere on the page. The datalist itself renders nothing visible — the browser only shows its options as suggestions once the input is focused or typed into.
Datalist vs Select — Not the Same Thing

It's tempting to think of <datalist> as "a <select> you can also type in," but the mental model that actually holds up is: it's a text input with hints. The value the form submits is whatever text is in the input — even if that text doesn't match any <option> in the datalist.

<select>

<input> + <datalist>

Value submitted

Must be one of the listed options

Any text the user types — options are just suggestions

Free text allowed?

No (unless combined with a hidden text input)

Yes, always

Keyboard entry

Jump-to-letter selection only

Full text editing, autocomplete-style filtering

Best for

A strict, closed set of choices

Open-ended input with common suggestions (city, tag, search term)

Never rely on it for validation
Because a visitor can type anything, never treat a datalist as a guarantee that the submitted value came from your list. If the value must be constrained to a fixed set, validate it (client- and server-side) or use a <select> instead.
Suggesting Values for Other Input Types

<datalist> isn't limited to type="text". It works with most text-like input types, including search, url, email, number, range, and color. For number and range, the suggested values appear as tick marks the browser lets the user snap to.

datalist-number.html

HTML
<label for="volume">Volume</label>
<input type="range" id="volume" name="volume" list="volume-marks" min="0" max="100" />

<datalist id="volume-marks">
  <option value="0"></option>
  <option value="25"></option>
  <option value="50"></option>
  <option value="75"></option>
  <option value="100"></option>
</datalist>
Options with a Separate Label

Each <option> can carry both a value (what gets filled into the input) and visible text or a label attribute (what shows in the dropdown next to the value). Browser support for the extra label text varies, so keep the value itself meaningful on its own.

datalist-labels.html

HTML
<label for="country-code">Country code</label>
<input type="text" id="country-code" name="country-code" list="codes" />

<datalist id="codes">
  <option value="+1">United States / Canada</option>
  <option value="+44">United Kingdom</option>
  <option value="+91">India</option>
  <option value="+81">Japan</option>
</datalist>
Populating a Datalist Dynamically

Because a datalist is just markup, JavaScript can rebuild its options at any time — a classic pattern for search-as-you-type suggestions fed from an API response.

datalist-dynamic.html

HTML
<input type="text" id="city" list="city-options" placeholder="Search a city" />
<datalist id="city-options"></datalist>

<script>
  const input = document.getElementById('city');
  const datalist = document.getElementById('city-options');

  input.addEventListener('input', async () => {
    const query = input.value.trim();
    if (query.length < 2) return;

    const results = await fetchCitySuggestions(query); // your own API call
    datalist.innerHTML = results
      .map((city) => `<option value="${city}"></option>`)
      .join('');
  });
</script>
Detecting Whether the User Picked a Suggestion

There's no built-in event for "user selected a datalist option." The practical workaround is comparing the input's current value against the list of known option values on the input or change event.

datalist-detect.html

HTML
<input type="text" id="fruit" list="fruits" />
<datalist id="fruits">
  <option value="Apple"></option>
  <option value="Banana"></option>
  <option value="Cherry"></option>
</datalist>

<script>
  const input = document.getElementById('fruit');
  const options = [...document.querySelectorAll('#fruits option')].map(
    (o) => o.value
  );

  input.addEventListener('change', () => {
    const matched = options.includes(input.value);
    console.log(matched ? 'Picked from the list' : 'Typed a custom value');
  });
</script>
Styling and Browser Behavior
  • The dropdown itself (the list of suggestions) is rendered by the browser natively and cannot be styled with CSS — its appearance differs across Chrome, Firefox, and Safari.

  • Safari has historically had weaker support for <datalist> than Chromium browsers and Firefox — always test the actual target browsers.

  • An empty <datalist> (no matching options) simply shows no dropdown; the input still works as plain text entry.

  • The <option> elements inside a datalist do not need a visible <label> in the DOM — the browser builds the suggestion list from value alone if no separate label text is given.

Great use cases
Search boxes with recent/popular queries, "pick a common tag but allow custom ones," unit or currency suggestions, and form fields where you want to nudge users toward consistent values without forbidding anything else.
Accessibility note
Always pair the input with a real <label> — the datalist provides suggestions, not an accessible name. Screen readers announce that suggestions are available, but the label is still what identifies the field.