If you ever want to build a searchable typeahead field, <datalist>
can do that for you. On all browsers. Across devices. Standards compliant. With no JavaScript. 🤯
TIL about <datalist>
— Swizec Teller (@Swizec) December 9, 2021
This bit of HTML gives you a full featured accessible typeahead search field 🤯 pic.twitter.com/PVoS0nTGLw
I learned about <datalist>
from a candidate in an interview. It was great.
Let's dig in.
The <datalist>
element
Searchable dropdown components are a combination of an <input>
field and a list of options. Typing filters the options so they're easier to find.
You get fuzzy search, keyboard navigation, mouse navigation, and enter to select. Dropdown appears on focus, hides on blur. The selected value is textual.
I got the list of dog breeds from the dog.ceo API.
An iPad with a keyboard attached shows this:
And it even works on a phone 🤩
You can try it yourself on CodeSandbox
How you can use <datalist>
You start with an input field:
<input name="dogBreed" list="breeds" />
The list
attribute defines which datalist you want to use. This is a globally unique identifier on the page.
Add a datalist component with your options:
<datalist id="breeds">
<option value="affenpinscher"></option>
<option value="african"></option>
<option value="airedale"></option>
<option value="akita"></option>
<option value="appenzeller"></option>
<option value="australian shepherd"></option>
<option value="basenji"></option>
<!-- ... -->
</datalist>
And that's it. The id
identifies your list and the browser handles the rest. You now have a standards compliant full featured typeahead field that works in any browser.
If you want to package this as a reusable component, you'll have to generate the id
and list
attributes on render. Needs to be unique on the page.
<datalist>
as a React component
A reusable React datalist needs to
- work as multiple instances on the page
- render options from a list
- return values with a callback
No. 3 is optional. You can do that using form machinery like react-hook-form.
You end up with a component like this:
const Datalist = ({ name, options, onChange }) => {
// this is a bad id for example purposes
// in React 18 you'd use useId()
const id = new Date().getTime()
return (
<>
<input name={name} list={id} onChange={onChange} />
<datalist id={id}>
{options.map((o) => (
<option value={o} />
))}
</datalist>
</>
)
}
We use a poor man's unique identifier – the current time in milliseconds. It's okay, but you'll want to use useId
when React 18 comes out and find a more robust solution until then.
We call onChange
for every input change. This may be annoying for consumers of our component.
A nice option would be to check the value against your list of options on every change and call an onSelected
callback when there's a match. That way you can catch when the user makes an explicit selection. Probably.
<datalist>
as a dynamic React component
If you have too many options to list or need richer search behavior, you could expand this component with a dynamic search. Like this:
const DynamicDatalist = ({ name, api, onChange }) => {
// this is a bad id for example purposes
// in React 18 you'd use useId()
const id = new Date().getTime()
const [options, setOptions] = useState([])
async function runSearch(event) {
const value = event.currentTarget.value
// you'll want to debounce this
const data = await fetchFrom(`${api}?search=${value}`)
setOptions(data.suggestions)
}
return (
<>
<input name={name} list={id} onChange={runSearch} />
<datalist id={id}>
{options.map((o) => (
<option value={o} />
))}
</datalist>
</>
)
}
Assume this is pseudocode.
You watch for changes, run API requests, and use those to populate the options list dynamically. Be careful about blindly running requests on every keystroke.
I gave it a shot here: https://t.co/IZqHvEEPjz. It seems to work as expected indeed! Thanks for the tip, I was looking for a vanilla HTML typeahead just yesterday.
— Eric Burel (@ericbureltech) December 10, 2021
What <datalist>
can't do
You can't style a datalist. I think this is strictly better.
In my experience those custom things always have a worse UX than the default browser component. I wish designers stopped insisting on customizing this stuff 😇
— Swizec Teller (@Swizec) December 9, 2021
You also can't use this to build the multiselect combobox thingy that's popular these days. The inputs that show your choices as pills next to your typing.
You can't use rich option values like objects. It's a text. Unless you build it yourself as part of a <Datalist>
component.
And you'll have to make your own validation that the final value is on the list. A normal <select>
might be a better option there.
The data list element is a text box with a list of suggestions. If you don’t support open ended text, it’s not useful.
— Ashley Ryan 👩🏼💻 (@ashleyepryan) December 10, 2021
It’s not a combobox/searchable select, which is something that tripped me up at first.
Next time you build a typehead, consider the <datalist>
. It might serve you better than a 81kb React library.
Cheers,
~Swizec
PS: <datalist>
works with Vue, Svelte, etc. I used React because that's more comfortable for me
Continue reading about Datalist, the pure HTML typeahead you didn't know about
Semantically similar articles hand-picked by GPT-4
- Fast, searchable dropdown inputs with React
- Why react-hook-form is my new favorite form library
- JavaScript’s most popular dataviz library
- React 18 and the future of async data
- TypeScript's biggest flaw and how you can use ducks to fix it
Learned something new?
Read more Software Engineering Lessons from Production
I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.
Subscribe below 👇
Software Engineering Lessons from Production
Join Swizec's Newsletter and get insightful emails 💌 on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.
"Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️