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
<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>
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) |
<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
<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
<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
<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
<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 fromvaluealone if no separate label text is given.
<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.