Swizec Teller - a geek with a hatswizec.com

Senior Mindset Book

Get promoted, earn a bigger salary, work for top companies

Senior Engineer Mindset cover
Learn more

    Datalist, the pure HTML typeahead you didn't know about

    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. 🤯

    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.

    Datalist in action

    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:

    Datalist on an iPad

    And it even works on a phone 🤩

    Datalist 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

    1. work as multiple instances on the page
    2. render options from a list
    3. 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.

    What <datalist> can't do

    You can't style a datalist. I think this is strictly better.

    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.

    A combobox multiselect, not possible with datalist
    A combobox multiselect, not possible with datalist

    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.

    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

    Published on December 14th, 2021 in Frontend, HTML

    Did you enjoy this article?

    Continue reading about Datalist, the pure HTML typeahead you didn't know about

    Semantically similar articles hand-picked by GPT-4

    Senior Mindset Book

    Get promoted, earn a bigger salary, work for top companies

    Learn more

    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 ❤️

    Created by Swizec with ❤️