Building accessible components with Downshift
Tags: a11y, javascript, react, tutorial
Published: 2020-05-13 14:32:32

---
title: Building accessible components with Downshift
published: true
date: 2020-04-20 13:00:56 UTC
tags: a11y,javascript,react,tutorial
canonical_url: https://blog.logrocket.com/building-accessible-components-with-downshift/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/y65r1luvj3xbmwa3wbz1.png
---
**Written by [Ganesh Mani](https://blog.logrocket.com/author/ganeshmani/)**✏️

The web has become so intertwined with our daily lives that we barely even notice it anymore. You probably use a web app for things as mundane as reserving a table at a restaurant, hailing a ride, booking a flight, even checking the weather.

Most of us would be hard-pressed to get through a day without interacting with some type of web application. That’s why it’s so important to make your apps accessible to all, including those with auditory, cognitive, neurological, physical, speech, visual, or other disabilities.

[Web accessibility](https://www.w3.org/WAI/standards-guidelines/wcag/) is often referred to as a11y, where the number 11 represents the number of letters omitted. As developers, we shouldn’t assume that all users interact with our applications same way. According to web standards such as [WAI-ARIA](https://www.w3.org/WAI/standards-guidelines/aria/), it’s our responsibility to make our web apps accessible to everyone.

Let’s look at a real-world example to illustrate the importance of web accessibility.

{% codesandbox pewvb %}

Consider using this HTML form without mouse. If you can easily complete your desired task, then you can consider the form accessible.

In this tutorial, we’ll demonstrate how to build accessible components using [Downshift](https://github.com/downshift-js/downshift). Downshift is a JavaScript library for building flexible, enhanced input components in React that comply with WAI-ARIA regulations.

Note: We’ll be using [React Hooks](https://dev.to/bnevilleoneill/developing-responsive-layouts-with-react-hooks-3cdo) in Downshift, so all the components will be built using Downshift hooks.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Select component

To build a simple and accessible select component, we’ll use a React Hook called `useSelect`, which is provided by Downshift.

![Select Component With Downshift](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/04/select-component-downshift.gif?resize=720%2C450&ssl=1)

Create a file called `DropDown.js` and add the following code.

```jsx
import React from "react";
import { useSelect } from "downshift";
import styled from "styled-components";
const DropDownContainer = styled.div`
  width: 200px;
`;
const DropDownHeader = styled.button`
  padding: 10px;
  display: flex;
  border-radius: 6px;
  border: 1px solid grey;
`;
const DropDownHeaderItemIcon = styled.div``;
const DropDownHeaderItem = styled.p``;
const DropDownList = styled.ul`
  max-height: "200px";
  overflow-y: "auto";
  width: "150px";
  margin: 0;
  border-top: 0;
  background: "white";
  list-style: none;
`;
const DropDownListItem = styled.li`
  padding: 5px;
  background: ${props => (props.ishighlighted ? "#A0AEC0" : "")};
  border-radius: 8px;
`;
const DropDown = ({ items }) => {
  const {
    isOpen,
    selectedItem,
    getToggleButtonProps,
    getMenuProps,
    highlightedIndex,
    getItemProps
  } = useSelect({ items });
  return (
    <DropDownContainer>
      <DropDownHeader {...getToggleButtonProps()}>
        {(selectedItem && selectedItem.value) || "Choose an Element"}
      </DropDownHeader>
      <DropDownList {...getMenuProps()}>
        {isOpen &&
          items.map((item, index) => (
            <DropDownListItem
              ishighlighted={highlightedIndex === index}
              key={`${item.id}${index}`}
              {...getItemProps({ item, index })}
            >
              {item.value}
            </DropDownListItem>
          ))}
      </DropDownList>
      <div tabIndex="0" />
    </DropDownContainer>
  );
};
export default DropDown;
```

Here, we have `styled-components` and `downshift` library. Styled components are used to create CSS in JavaScript.

We also have the `useSelect` hook, which takes the items array as an argument and returns a few props, including the following.

- [`isOpen`](https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect#isopen) helps to maintain the state of the menu. If the menu is expanded, `isOpen` will be true. If is collapsed, it will return false
- [`selectedItem`](https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect#isopen) returns the selected item from the list
- [`getToggleButtonProps`](https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect#gettogglebuttonprops) provides an input button that we need to bind with our toggle button (it can be an input or a button)
- [`getMenuProps`](https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect#getmenuprops) provides the props for the menu. We can bind this with a div or UI element
- [`getItemProps`](https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect#getitemprops) returns the props we need to bind with the menu list item
- [`highlightedIndex`](https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect#highlightedindex) returns the index of a selected array element and enables you to style the element while rendering

Below are some other props that `useSelect` provides.

- [`onStateChange`](https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect#onstatechange) is called anytime the internal state change. In simple terms, you can manage states such as `isOpen` and `SelectedItem` in your component state using this function
- [`itemToString`](https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect#itemtostring) — If your array items is an object, `selectedItem` will return the object instead of a string value. For example:

```jsx
selectedItem : { id : 1,value : "Sample"}
```

Since we cannot render it like this, we can convert it into a string using the `itemToString` props.

First, render the button that handles the toggle button of the select component.

```jsx
{(selectedItem && selectedItem.value) || "Choose an Element"}
```

After that, render the menu and menu items with the Downshift props.

```jsx
<DropDownList {...getMenuProps()}>
        {isOpen &&
          items.map((item, index) => (
            <DropDownListItem
              ishighlighted={highlightedIndex === index}
              key={`${item.id}${index}`}
              {...getItemProps({ item, index })}
            >
              {item.value}
            </DropDownListItem>
          ))}
</DropDownList>
```

{% codesandbox 20fe3 %}

## Autocomplete component

Autocomplete works in the same way as the select component except it has search functionality. Let’s walk through how to build an autocomplete component using downshift.

![Autocomplete Component With Downshift](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/04/autocomplete-component-downshift.gif?resize=720%2C450&ssl=1)

Unlike Downshift, the autocomplete component uses the `useCombobox` hook.

```jsx
import React,{ useState } from 'react';
import { IconButton,Avatar,Icon } from '@chakra-ui/core';
import { useCombobox } from 'downshift';
import styled from "styled-components";
const Input = styled.input`
  width: 80px;
  border: 1px solid black;
  display :  ${({ isActive }) => isActive ? 'block' : 'none'}
  border-bottom-left-radius: ${({ isActive }) => isActive && 0};
  border-bottom-right-radius: ${({ isActive }) => isActive && 0};
  border-radius: 3px;
`;

const SelectHook = ({
  items,
  onChange,
  menuStyles
}) => {
  const [inputItems, setInputItems] = useState(items);
  const {
    isOpen,
    getToggleButtonProps,
    getLabelProps,
    getMenuProps,
    getInputProps,
    getComboboxProps,
    highlightedIndex,
    getItemProps,
    onStateChange,
    onSelectedItemChange,
    selectedItem,
    itemToString
  } = useCombobox({ 
    items: inputItems,
    itemToString : item => (item ? item.value : ""),
    onInputValueChange: ({ inputValue }) => {
      let inputItem = items.filter(item => {

         return item.value.toLowerCase().startsWith(inputValue.toLowerCase())
      }
        );

      setInputItems(inputItem)
    },
    onStateChange : (state) => {
      console.log("state",state);
      if(state.inputValue){
          onChange(state.selectedItem);
      }
      if(!state.isOpen){
          return {
            ...state,
            selectedItem : ""
          }
      }

    }
     });

   return (
      <div>
       <label {...getLabelProps()}>Choose an element:</label> 
      <div {...getToggleButtonProps()}>
       <Avatar name="Kent Dodds" src="https://bit.ly/kent-c-dodds"/>
       </div>
      <div style={{ display: "inline-block" }} {...getComboboxProps()}>
        <Input {...getInputProps()} isActive={isOpen} />
      </div>
      <ul {...getMenuProps()} style={menuStyles}>
        {isOpen &&
          inputItems.map((item, index) => (
            <li
              style={
                highlightedIndex === index ? { backgroundColor: "#bde4ff" } : {}
              }
              key={`${item}${index}`}
              {...getItemProps({ item, index })}
            >
              {item.value}
            </li>
          ))}
      </ul>
    </div>
   )
}
export default SelectHook;
```

`useCombobox` takes the items array as an input as well as some other props we discussed in the previous component. `useCombobox` provides the following props.

- `getComboboxProps` is a wrapper of input element in the select component that provides combobox props from Downshift.
- `onInputValueChange` is called when the value of the input element changes. You can manage the state of the input element in the component itself through this event callback

Let’s break down the component and try to understand its logic.

The component takes three props:

1. `items`, which represents the input element array
2. `onChange`, which is called when selected item changes
3. `menuStyles`, which this is optional; you can either pass it as props or run the following

```jsx
const SelectHook = ({
items,
onChange,
menuStyles
}) => { }
```

Now we have state value, which maintains the input value and `useCombobox` hook.

```jsx
const [inputItems, setInputItems] = useState(items);

  const {
    isOpen,
    getToggleButtonProps,
    getLabelProps,
    getMenuProps,
    getInputProps,
    getComboboxProps,
    highlightedIndex,
    getItemProps,
    onStateChange,
    onSelectedItemChange,
    selectedItem,
    itemToString
  } = useCombobox({ 
    items: inputItems,
    itemToString : item => (item ? item.value : ""),
    onInputValueChange: ({ inputValue }) => {
      let inputItem = items.filter(item => {

         return item.value.toLowerCase().startsWith(inputValue.toLowerCase())
      }
        );

      setInputItems(inputItem)
    },
    onStateChange : (state) => {
      if(state.inputValue){
          onChange(state.selectedItem);
      }
      if(!state.isOpen){
          return {
            ...state,
            selectedItem : ""
          }
      }

    }
     });
```

Once we set up the hook, we can use all the props it provides for the autocomplete component.

Let’s start from the toggle button props. Set it up for any element you want to use as toggler.

```jsx
<div {...getToggleButtonProps()}>
   <Avatar name="Kent Dodds" src="https://bit.ly/kent-c-dodds"/>
 </div>
```

This gives us an input element that we need to render along with dropdown.

```jsx
<div style={{ display: "inline-block" }} {...getComboboxProps()}>
    <Input {...getInputProps()} isActive={isOpen} />
</div>
```

Finally, we have list and list item that takes Downshift props such as `getMenuProps` and `getItemProps`.

```jsx
<ul {...getMenuProps()} style={menuStyles}>
        {isOpen &&
          inputItems.map((item, index) => (
            <li
              style={
                highlightedIndex === index ? { backgroundColor: "#bde4ff" } : {}
              }
              key={`${item}${index}`}
              {...getItemProps({ item, index })}
            >
              {item.value}
            </li>
          ))}
</ul>
```

{% stackblitz react-downshift-select-idywah %}

## Dropdown form

In this section, we’ll demonstrate how to use Downshift with the dropdown in your form.

![Form Dropdown With Downshift](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/04/dropdown-form-downshift.gif?resize=720%2C450&ssl=1)

Here we have two components: `DownshiftInput.js` for the autocomplete component  
and `App.js`, which handles the form.

First, implement `DownshiftInput.js`.

```jsx
import React, { useState } from "react";
import styled from "styled-components";
import { useCombobox } from "downshift";
const DropDownContainer = styled.div`
  width: 100%;
`;
const DropDownInput = styled.input`
  width: 100%;
  height: 20px;
  border-radius: 8px;
`;
const DropDownInputLabel = styled.label`
  padding: 5px;
`;
const DropDownMenu = styled.ul`
  max-height: "180px";
  overflow-y: "auto";
  width: "90px";
  border-top: 0;
  background: "white";
  position: "absolute";
  list-style: none;
  padding: 0;
`;
const DropDownMenuItem = styled.li`
  padding: 8px;
  background-color: ${props => (props.ishighlighted ? "#bde4ff" : "")};
  border-radius: 8px;
`;
const DownshiftInput = ({ items, onChange, labelName }) => {
  const [inputItems, setInputItems] = useState(items);
  const [inputValue, setInputValue] = useState("");
  const {
    isOpen,
    getInputProps,
    getLabelProps,
    getItemProps,
    getMenuProps,
    highlightedIndex
  } = useCombobox({
    items,
    itemToString: item => {
      return item && item.value;
    },
    onInputValueChange: ({ inputValue }) => {
      let inputItem = items.filter(item => {
        return item.value.toLowerCase().startsWith(inputValue.toLowerCase());
      });
      setInputItems(inputItem);
      setInputValue(inputValue);
    },
    onSelectedItemChange: ({ selectedItem }) => {
      onChange(selectedItem);
      setInputValue(selectedItem.value);
    }
  });
  return (
    <DropDownContainer>
      <DropDownInputLabel {...getLabelProps()}>{labelName}</DropDownInputLabel>
      <DropDownInput
        {...getInputProps({
          value: inputValue
        })}
      />
      <DropDownMenu {...getMenuProps()}>
        {isOpen &&
          inputItems.map((item, index) => (
            <DropDownMenuItem
              ishighlighted={highlightedIndex === index}
              key={`${item}${index}`}
              {...getItemProps({ item, index })}
            >
              {item.value}
            </DropDownMenuItem>
          ))}
      </DropDownMenu>
    </DropDownContainer>
  );
};
export default DownshiftInput;
```

Here we implemented the same logic that we used in the autocomplete component, the `useCombobox` hook.

Props that we used in this component include:

- `isOpen`, which is used to manage the state of the menu
- `getInputProps`, which should bind with input element
- `getLabelProps` to map with labels
- `getItemProps`, which is used to bind the Downshift props with menu items
- `getMenuProps`, which is used for mapping the downshift with our menu
- `highlightedIndex`, which returns the highlighted element index

Downshift event callbacks from the hook include:

- `onInputValueChange`, which returns the `inputValue` from the input element
- `onSelectedItemChange`, which is called when a selected item changes

`App.js`:

```jsx
import React, { useState } from "react";
import "./styles.css";
import styled from "styled-components";
import DownshiftInput from "./DownshiftInput";
const Container = styled.div`
  width: 50%;
  margin: auto;
  top: 50%;
  /* transform: translateY(-50%); */
`;
const ContainerHeader = styled.h2``;
const Form = styled.form`
  /* border: 3px solid grey; */
`;
const FormButton = styled.button`
  width: 100%;
  padding: 8px;
  background-color: #718096;
  border-radius: 8px;
`;
export default function App() {
  const [state, setState] = useState({
    item: {},
    element: {}
  });
  const items = [
    { id: "1", value: "One" },
    { id: "2", value: "Two" },
    { id: "3", value: "Three" },
    { id: "4", value: "Four" },
    { id: "5", value: "Five" }
  ];
  const onItemChange = value => {
    setState({ ...state, item: value });
  };
  const onElementChange = value => {
    setState({ ...state, element: value });
  };
  const onSubmit = e => {
    e.preventDefault();
    console.log("submitted", state);
    alert(`item is:${state.item.value} and Element is ${state.element.value}`);
  };
  return (
    <Container>
      <ContainerHeader>Downshift Form</ContainerHeader>
      <Form onSubmit={onSubmit}>
        <DownshiftInput
          items={items}
          onChange={onItemChange}
          labelName="Select Item"
        />
        <DownshiftInput
          items={items}
          onChange={onElementChange}
          labelName="Choose an Element"
        />
        <FormButton>Submit</FormButton>
      </Form>
    </Container>
  );
}
```

{% codesandbox 6rber %}

## Chat mentions

The final step is to build a chat box mentions feature. We can do this using Downshift.

Here’s an example of the finished product:

![Chat Mentions](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/04/chat-mentions.png?resize=720%2C351&ssl=1)

A dropdown opens on top of an input element. It’s a handy feature that mentions the user in the message.

![Dropdown Form With React Popper](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/04/dropdown-react-popper.gif?resize=720%2C450&ssl=1)

To place the dropdown on top of the input, we’ll use React Popper along with Downshift.

Let’s review the three most important concepts associated with Popper before building the component.

1. `Manager` — All the react popper components should be wrapped inside the manager component
2. `Reference` — React Popper uses the reference component to manage the popper. If you use a button as a reference, the popper opens or closes based on the button component
3. `Popper` — This manages what should be rendered on Popper. Popper opens the custom component based on a different action, such as button click or input change

Let’s create a component called `MentionComponent.js` and add the following code.

```jsx
import React, { useState } from "react";
import { useCombobox } from "downshift";
import styled from "styled-components";
import { Popper, Manager, Reference } from "react-popper";
const Container = styled.div``;
const DropDownInput = styled.input``;
const DropDownMenu = styled.ul`
  max-height: "180px";
  overflow-y: "auto";
  width: "90px";
  border-top: 0;
  background: "blue";
  position: "absolute";
  list-style: none;
  padding: 0;
`;
const DropDownMenuItem = styled.li`
  padding: 8px;
  background-color: ${props => (props.ishighlighted ? "#bde4ff" : "")};
  border-radius: 8px;
`;
const MentionComponent = ({ items }) => {
  const [inputItems, setInputItems] = useState(items);
  const {
    isOpen,
    getInputProps,
    getItemProps,
    getMenuProps,
    highlightedIndex
  } = useCombobox({
    items,
    itemToString: item => {
      console.log("item", item);
      return item ? item.value : null;
    },
    onInputValueChange: ({ inputValue }) => {
      let inputItem = items.filter(item => {
        return item.value.toLowerCase().startsWith(inputValue.toLowerCase());
      });
      setInputItems(inputItem);
    }
  });
  return (
    <Container>
      <Manager>
        <Reference>
          {/* {({ ref }) => (

          )} */}
          {({ ref }) => (
            <div
              style={{
                width: "20%",
                margin: "auto",
                display: "flex",
                alignItems: "flex-end",
                height: "50vh"
              }}
              // ref={ref}
            >
              <DropDownInput
                ref={ref}
                {...getInputProps({
                  placeholder: "Enter Value",
                  style: {
                    width: "100%",
                    padding: "8px",
                    borderRadius: "6px",
                    border: "1px solid grey"
                  }
                })}
              />
            </div>
          )}
        </Reference>
        {isOpen ? (
          <Popper placement="top">
            {({
              ref: setPopperRef,
              style,
              placement,
              arrowProps,
              scheduleUpdate
            }) => {
              return (
                <DropDownMenu
                  {...getMenuProps({
                    ref: ref => {
                      if (ref !== null) {
                        setPopperRef(ref);
                      }
                    },
                    style: {
                      ...style,
                      background: "grey",
                      opacity: 1,
                      top: "10%",
                      left: "40%",
                      width: "20%"
                    },
                    "data-placement": placement
                  })}
                >
                  {isOpen &&
                    inputItems.map((item, index) => (
                      <DropDownMenuItem
                        ishighlighted={highlightedIndex === index}
                        key={`${item}${index}`}
                        {...getItemProps({ item, index })}
                      >
                        {item.value}
                      </DropDownMenuItem>
                    ))}
                </DropDownMenu>
              );
            }}
          </Popper>
        ) : null}
      </Manager>
    </Container>
  );
};
export default MentionComponent;
```

Let’s break down each part one by one. Everything associated with React Popper should be wrapped inside the `Manager` component.

After that, the `Reference` component wraps the `Input` element.

```jsx
<Reference>
          {({ ref }) => (
            <div
              style={{
                width: "20%",
                margin: "auto",
                display: "flex",
                alignItems: "flex-end",
                height: "50vh"
              }}
              // ref={ref}
            >
              <DropDownInput
                ref={ref}
                {...getInputProps({
                  placeholder: "Enter Value",
                  style: {
                    width: "100%",
                    padding: "8px",
                    borderRadius: "6px",
                    border: "1px solid grey"
                  }
                })}
              />
            </div>
          )}
        </Reference>
```

Here we implemented `getInputProps` from Downshift and binded it with an input element.

The popper itself contains the menu and menu items with Downshift props such as `getMenuProps` and `getItemProps`.

```jsx
{isOpen ? (
          <Popper placement="top">
            {({
              ref: setPopperRef,
              style,
              placement,
              arrowProps,
              scheduleUpdate
            }) => {
              return (
                <DropDownMenu
                  {...getMenuProps({
                    ref: ref => {
                      if (ref !== null) {
                        setPopperRef(ref);
                      }
                    },
                    style: {
                      ...style,
                      background: "grey",
                      opacity: 1,
                      top: "10%",
                      left: "40%",
                      width: "20%"
                    },
                    "data-placement": placement
                  })}
                >
                  {isOpen &&
                    inputItems.map((item, index) => (
                      <DropDownMenuItem
                        ishighlighted={highlightedIndex === index}
                        key={`${item}${index}`}
                        {...getItemProps({ item, index })}
                      >
                        {item.value}
                      </DropDownMenuItem>
                    ))}
                </DropDownMenu>
              );
            }}
          </Popper>
        ) : null}
```

We use the Downshift hook `useCombobox` like we used it in the autocomplete component. Most of the logic is same except that we’ll wrap it inside `popper.js`.

<iframe style="width: 100%; height: 500px; border: 0; border-radius: 4px; overflow: hidden;" title="downshift-mention" src="https://codesandbox.io/embed/downshift-mention-jtnfv?fontsize=14&amp;hidenavigation=1&amp;theme=dark" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>

## Summary

You should now have the basic tools and knowledge to build accessible components into your apps using Downshift. To summarize, we covered how to build an accessible simple select component, accessible autocomplete, and form dropdown as well as how to use Downshift with Popper.js.

In my point of view, we shouldn’t view web accessibility as a feature; we should consider it our responsibility to make the web accessible to everyone.

* * * 
## Full visibility into production React apps 

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, [try LogRocket.](https://www2.logrocket.com/react-performance-monitoring)

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/eq752g8qhbffxt3hp9t4.png)

[LogRocket](https://www2.logrocket.com/react-performance-monitoring) is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — [start monitoring for free.](https://www2.logrocket.com/react-performance-monitoring)
* * *

The post [Building accessible components with Downshift](https://blog.logrocket.com/building-accessible-components-with-downshift/) appeared first on [LogRocket Blog](https://blog.logrocket.com).