diff --git a/src/components/drops/menu/dropdown.js b/src/components/drops/menu/dropdown.js
index 1886c7ee..11e890c1 100644
--- a/src/components/drops/menu/dropdown.js
+++ b/src/components/drops/menu/dropdown.js
@@ -5,6 +5,7 @@ import Flex from "@/components/templates/flex"
 import Search from "@/components/search"
 import Box from "@/components/templates/box"
 import { mergeRefs } from "@/utils"
+import { useCallback } from "react"
 
 const Container = styled(Flex)`
   ${({ hideShadow }) =>
@@ -15,6 +16,19 @@ const Container = styled(Flex)`
 
 const defaultEstimateSize = () => 28
 
+const indexCalculatorByKey = {
+  ArrowDown: (index, length) => Math.min(index + 1, length - 1),
+  ArrowUp: index => Math.max(index - 1, 0),
+  Home: () => 0,
+  End: (_, length) => length - 1,
+  default: index => index,
+}
+
+const getNextIndex = (currentIndex, key, itemsLength) => {
+  const calculator = indexCalculatorByKey[key] || indexCalculatorByKey.default
+  return calculator(currentIndex, itemsLength)
+}
+
 const Dropdown = forwardRef(
   (
     {
@@ -32,6 +46,9 @@ const Dropdown = forwardRef(
       gap = 0,
       estimateSize = defaultEstimateSize,
       close,
+      enableKeyNavigation,
+      activeIndex,
+      setActiveIndex,
       ...rest
     },
     forwardedRef
@@ -61,6 +78,31 @@ const Dropdown = forwardRef(
       estimateSize,
     })
 
+    const handleKeyDown = useCallback(
+      event => {
+        if (["ArrowDown", "ArrowUp", "Home", "End"].includes(event.code)) {
+          setActiveIndex(prevIndex => {
+            const nextIndex = getNextIndex(prevIndex, event.code, items.length)
+            rowVirtualizer.scrollToIndex(nextIndex)
+            return nextIndex
+          })
+        }
+      },
+      [rowVirtualizer, items, setActiveIndex]
+    )
+
+    const virtualContainerProps = useMemo(() => {
+      if (enableKeyNavigation)
+        return {
+          tabIndex: 0,
+          role: "listbox",
+          "aria-activedescendant": `item-${activeIndex}`,
+          onKeyDown: handleKeyDown,
+        }
+
+      return {}
+    }, [enableKeyNavigation, activeIndex, handleKeyDown])
+
     return (
       <Container
         as="ul"
@@ -86,6 +128,7 @@ const Dropdown = forwardRef(
             height: "100%",
             overflow: "auto",
           }}
+          {...virtualContainerProps}
         >
           <div
             style={{
@@ -116,6 +159,7 @@ const Dropdown = forwardRef(
                   value={value}
                   onItemClick={onItemClick}
                   close={close}
+                  {...(enableKeyNavigation ? { enableKeyNavigation: true, activeIndex } : {})}
                 />
               </div>
             ))}
diff --git a/src/components/drops/menu/dropdownItem.js b/src/components/drops/menu/dropdownItem.js
index 4aae3bb8..8ba2b1ef 100644
--- a/src/components/drops/menu/dropdownItem.js
+++ b/src/components/drops/menu/dropdownItem.js
@@ -13,6 +13,8 @@ export const ItemContainer = styled(Flex).attrs(props => ({
   cursor: ${({ cursor }) => cursor ?? "pointer"};
   opacity: ${({ disabled, selected }) => (selected ? 0.9 : disabled ? 0.4 : 1)};
   pointer-events: ${({ disabled }) => (disabled ? "none" : "auto")};
+  background-color: ${props =>
+    props.activeIndex == props.index ? getColor("borderSecondary")(props) : "none"};
 
   &:hover {
     background-color: ${props => getColor("borderSecondary")(props)};
@@ -43,6 +45,7 @@ const DropdownItem = ({
   onItemClick,
   index,
   style,
+  enableKeyNavigation,
   ...rest
 }) => {
   const selected = selectedValue === value
@@ -54,11 +57,14 @@ const DropdownItem = ({
 
   return (
     <ItemContainer
+      id={`item-${index}`}
       data-index={index}
       aria-selected={selected}
       disabled={disabled}
       selected={selected}
       onClick={onSelect}
+      index={index}
+      {...(enableKeyNavigation ? { role: "option" } : {})}
       {...restItem}
       {...rest}
       style={style}
diff --git a/src/components/input/autocomplete/index.js b/src/components/input/autocomplete/index.js
new file mode 100644
index 00000000..d0dc7494
--- /dev/null
+++ b/src/components/input/autocomplete/index.js
@@ -0,0 +1,50 @@
+import React, { forwardRef, useCallback, useState } from "react"
+import { StyledOptionsContainer } from "./styled"
+import Dropdown from "@/components/drops/menu/dropdown"
+import DropdownItem from "@/components/drops/menu/dropdownItem"
+import useOutsideClick from "@/hooks/useOutsideClick"
+import useAutocomplete from "./useAutocomplete"
+
+const Autocomplete = forwardRef(({ value, autocompleteProps, onInputChange, onEsc }, ref) => {
+  const [activeIndex, setActiveIndex] = useState(0)
+  const { autocompleteOpen, close, filteredSuggestions, onItemClick } = useAutocomplete({
+    value,
+    onInputChange,
+    autocompleteProps,
+  })
+
+  const onKeyDown = useCallback(
+    e => {
+      if (e.code == "Escape") {
+        onEsc()
+        close()
+      } else if (e.code == "Enter") {
+        onItemClick(filteredSuggestions[activeIndex]?.value)
+        onEsc()
+      }
+    },
+    [activeIndex, filteredSuggestions, onItemClick, onEsc, close]
+  )
+
+  useOutsideClick(ref, close, ref?.current)
+
+  return (
+    autocompleteOpen && (
+      <StyledOptionsContainer>
+        <Dropdown
+          ref={ref}
+          items={filteredSuggestions}
+          Item={DropdownItem}
+          onItemClick={onItemClick}
+          width="100%"
+          onKeyDown={onKeyDown}
+          enableKeyNavigation
+          activeIndex={activeIndex}
+          setActiveIndex={setActiveIndex}
+        />
+      </StyledOptionsContainer>
+    )
+  )
+})
+
+export default Autocomplete
diff --git a/src/components/input/autocomplete/styled.js b/src/components/input/autocomplete/styled.js
new file mode 100644
index 00000000..5b2096b2
--- /dev/null
+++ b/src/components/input/autocomplete/styled.js
@@ -0,0 +1,10 @@
+import styled from "styled-components"
+import Flex from "@/components/templates/flex"
+
+export const StyledOptionsContainer = styled(Flex)`
+  width: 300px;
+  max-height: 300px;
+  position: absolute;
+  left: 0;
+  top: 36px;
+`
diff --git a/src/components/input/autocomplete/useAutocomplete.js b/src/components/input/autocomplete/useAutocomplete.js
new file mode 100644
index 00000000..6e61bad7
--- /dev/null
+++ b/src/components/input/autocomplete/useAutocomplete.js
@@ -0,0 +1,50 @@
+import { useState, useEffect, useMemo, useCallback } from "react"
+
+const defaultSuggestions = {
+  loading: false,
+  loaded: true,
+  value: [],
+  error: null,
+}
+
+const useAutocomplete = ({ value, onInputChange, autocompleteProps = {} }) => {
+  const [autocompleteOpen, setAutocompleteOpen] = useState()
+  const { suggestions = defaultSuggestions } = autocompleteProps || {}
+  const items = useMemo(
+    () =>
+      suggestions.value.map(suggestion => ({
+        value: suggestion,
+        label: suggestion,
+      })),
+    [suggestions]
+  )
+  const [filteredSuggestions, setFilteredSuggestions] = useState(items)
+
+  const close = useCallback(() => setAutocompleteOpen(false), [setAutocompleteOpen])
+
+  const onItemClick = useCallback(
+    val => {
+      if (typeof onInputChange == "function") {
+        onInputChange({ target: { value: val } })
+        setTimeout(() => close(), 100)
+      }
+    },
+    [close, onInputChange]
+  )
+
+  useEffect(() => {
+    if (!value) {
+      close()
+    } else if (items.length) {
+      const filtered = items.filter(({ label }) =>
+        label.toLowerCase().includes(value.toLowerCase())
+      )
+      setFilteredSuggestions(filtered)
+      setAutocompleteOpen(!!filtered.length)
+    }
+  }, [value, items, setAutocompleteOpen, setFilteredSuggestions, close])
+
+  return { autocompleteOpen, close, filteredSuggestions, onItemClick }
+}
+
+export default useAutocomplete
diff --git a/src/components/input/input.js b/src/components/input/input.js
index b0ea557e..9b979236 100644
--- a/src/components/input/input.js
+++ b/src/components/input/input.js
@@ -1,7 +1,9 @@
-import React from "react"
+import React, { useMemo, useRef, useCallback } from "react"
 import Flex from "@/components/templates/flex"
 import { TextMicro } from "@/components/typography"
 import { Input, LabelText } from "./styled"
+import Autocomplete from "./autocomplete"
+import { mergeRefs } from "@/utils"
 
 const Error = ({ error }) => {
   const errorMessage = error === true ? "invalid" : error
@@ -32,10 +34,48 @@ export const TextInput = ({
   containerStyles,
   inputContainerStyles,
   hideErrorMessage,
+  autocompleteProps,
   ...props
 }) => {
+  const ref = useRef()
+  const autocompleteMenuRef = useRef()
+
+  const onKeyDown = useCallback(
+    e => {
+      if (autocompleteMenuRef.current && ["ArrowDown", "ArrowUp"].includes(e.key)) {
+        autocompleteMenuRef.current.focus()
+      }
+    },
+    [autocompleteMenuRef?.current]
+  )
+
+  const onAutocompleteEscape = useCallback(() => {
+    if (ref?.current) {
+      ref.current.focus()
+    }
+  }, [ref])
+
+  const autocompleteInputProps = useMemo(
+    () =>
+      autocompleteProps
+        ? {
+            "aria-autocomplete": "list",
+            "aria-controls": "autocomplete-list",
+            onKeyDown,
+          }
+        : {},
+    [autocompleteProps, onKeyDown]
+  )
+
   return (
-    <Flex gap={0.5} column className={className} {...containerStyles} as="label">
+    <Flex
+      gap={0.5}
+      column
+      className={className}
+      position="relative"
+      {...containerStyles}
+      as="label"
+    >
       {typeof label === "string" ? <LabelText size={size}>{label}</LabelText> : label}
       <Flex position="relative" {...inputContainerStyles}>
         {iconLeft && (
@@ -56,9 +96,10 @@ export const TextInput = ({
           type="text"
           value={value}
           size={size}
-          ref={inputRef}
+          ref={mergeRefs(inputRef, ref)}
           error={error}
           hasValue={!!value}
+          {...autocompleteInputProps}
           {...props}
         />
 
@@ -71,6 +112,13 @@ export const TextInput = ({
       </Flex>
       {typeof hint === "string" ? <TextMicro color="textLite">{hint}</TextMicro> : !!hint && hint}
       {!hideErrorMessage ? <Error error={error} /> : null}
+      <Autocomplete
+        ref={autocompleteMenuRef}
+        value={value}
+        onEsc={onAutocompleteEscape}
+        autocompleteProps={autocompleteProps}
+        onInputChange={props.onChange}
+      />
     </Flex>
   )
 }
diff --git a/src/components/input/input.stories.js b/src/components/input/input.stories.js
index e917fd82..dd969b9a 100644
--- a/src/components/input/input.stories.js
+++ b/src/components/input/input.stories.js
@@ -1,6 +1,7 @@
 import React from "react"
 import { Icon } from "@/components/icon"
 import { TextInput } from "."
+import { useState } from "react"
 
 export const WithIcons = args => (
   <TextInput
@@ -12,6 +13,23 @@ export const WithIcons = args => (
 
 export const Basic = args => <TextInput {...args} />
 
+export const WithAutocomplete = () => {
+  const [value, setValue] = useState("")
+  const autocompleteProps = {
+    suggestions: {
+      loading: false,
+      value: Array.from(Array(10000).keys()).map(i => `Label ${i}`),
+      error: null,
+    },
+  }
+
+  const onChange = e => {
+    setValue(e.target.value)
+  }
+
+  return <TextInput value={value} onChange={onChange} autocompleteProps={autocompleteProps} />
+}
+
 export default {
   component: TextInput,
   args: {