Autocomplete.js is a JavaScript library that creates a fast and fully-featured auto-completion experience.
Contents
- Displays suggestions as you type
- Shows top suggestion as a completion
- Supports custom templates for UI flexibility
- Works well with RTL languages
- Triggers custom hooks to plug your logic
- Plugs easily to Algolia's realtime search engine
<body>
<div id="autocomplete"></div>
</body>
const items = [
{ value: 'Apple', count: 120 },
{ value: 'Banana', count: 100 },
{ value: 'Cherry', count: 50 },
{ value: 'Orange', count: 150 },
];
autocomplete({
container: '#autocomplete',
getSources() {
return [
{
getSuggestions({ query }) {
return items.filter((item) =>
item.value.toLocaleLowerCase().includes(query.toLocaleLowerCase())
);
},
getInputValue({ suggestion }) {
return suggestion.value;
},
templates: {
suggestion({ suggestion }) {
return `<div>${suggestion.value} (${suggestion.count})</div>`;
},
},
},
];
},
});
You can learn more about the options and the top-level API.
Autocomplete.js is available on the npm registry.
yarn add @francoischalifour/autocomplete.js@alpha
# or
npm install @francoischalifour/autocomplete.js@alpha
If you do not wish to use a package manager, you can use standalone endpoints:
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@francoischalifour/autocomplete.js@alpha"></script>
<!-- unpkg -->
<script src="https://unpkg.com/@francoischalifour/autocomplete.js@alpha"></script>
string | HTMLElement
| required
The container for the autocomplete search box.
(params: { query: string }) => AutocompleteSource[] | Promise<AutocompleteSource[]>
Called to fetch the sources.
string | HTMLElement
| defaults todocument.body
The container for the autocomplete dropdown.
'start' | 'end'
| defaults to'start'
The dropdown placement related to the container.
(params: { containerRect: ClientRect, dropdownPosition: DropdownPosition }) => DropdownPosition
| defaults to({ dropdownPosition }) => dropdownPosition
Called to compute the dropdown position. This function is called at first load and when the window is resized.
DropdownPosition
definition
interface DropdownPosition {
top: number;
left?: number;
right?: number;
}
Example
Removing margins on mobile
autocomplete({
// ...
getDropdownPosition({ dropdownPosition }) {
// Desktop: we want to return the dropdown position as is.
if (window.matchMedia('(min-width: 650px)').matches) {
return dropdownPosition;
}
// Mobile: we want to return the dropdown position without left or right
// margins.
return { top: dropdownPosition.top };
},
});
string
| defaults to""
The text that appears in the search box input when there is no query.
It is fowarded to the input
's placeholder.
boolean
| defaults tofalse
Whether to show the highlighted suggestion as completion in the input.
boolean
| defaults tofalse
Whether to open the dropdown on focus when there's no query.
boolean
| defaults tofalse
Whether to focus the search box when the page is loaded.
string[]
The keyboard shortcuts keys to focus the input.
number | null
| defaults tonull
The default item index to pre-select.
number
| defaults to300
The number of milliseconds that must elapse before the autocomplete experience is stalled. The timeout is set from the moment getSources
is called.
When the experience is stalled:
- The CSS class
algolia-autocomplete--stalled
is added to the autocomplete container - The
status
state is set to"stalled"
in the state
The initial state to apply when the page is loaded.
Refer to the "Global Templates" section.
(results: JSX.Element[]) => JSX.Element | JSX.Element[]
Called before rendering the results.
Useful to wrap results in containers to organize the display.
Example
autocomplete({
// ...
transformResultsRender(results) {
const [recentSearches, querySuggestions, products] = results;
return (
<div style={{ display: 'flex' }}>
<div style={{ flex: 1 }}>
{recentSearches}
{querySuggestions}
</div>
<div style={{ flex: 2 }}>{products}</div>
</div>
);
},
});
typeof window
| defaults towindow
The environment from where your JavaScript is running.
Useful if you're using Autocomplete.js in a different context than window
.
Navigator
API used to redirect users when a suggestion link is open programmatically (using keyboard navigation). It defines how a URL should be open in the current tab, in a new tab and in a new window.
The source needs to specify getSuggestionUrl
for the suggestion URL to be provided.
Example
autocomplete({
// ...
navigator: {
navigate({ suggestionUrl }) {
environment.location.assign(suggestionUrl);
},
navigateNewTab({ suggestionUrl }) {
const windowReference = environment.open(suggestionUrl, '_blank');
if (windowReference) {
windowReference.focus();
}
},
navigateNewWindow({ suggestionUrl }) {
environment.open(suggestionUrl, '_blank');
},
},
});
(params: { state: AutocompleteState, ...setters }) => void
Called when the input is focused.
This function is also called when the input is clicked while already having the focus and the dropdown is closed.
(params: { state: AutocompleteState, ...setters }) => void
| defaults to({ state }) => throw state.error
Called when an error is thrown while getting the suggestions.
When an error is caught:
- The error is thrown (default
onError
implementation) - The CSS class
algolia-autocomplete--errored
is added to the autocomplete container - The error is available in the state
(event: MouseEvent, params: { state: AutocompleteState, ...setters, suggestion: any, suggestionValue: string }) => void
Called when a click
event is fired on an item.
This function is useful to alter the behavior when a special key is held (e.g. keeping the dropdown open when the meta key is used).
(event: KeyboardEvent, options: { state: AutocompleteState, ...setters, suggestion?: any, suggestionValue?: string, suggestionUrl?: string }) => void
| defaults to an accessible behavior
Called when a keydown
event is fired.
This function is useful to alter the behavior when a special key is held.
Example
autocomplete({
// ...
onKeyDown(event, { suggestionUrl, suggestion }) {
if (!suggestionUrl) {
return;
}
if (event.key === 'Enter') {
if (event.metaKey || event.ctrlKey) {
const windowReference = window.open(suggestion.url, '_blank');
windowReference.focus();
} else if (event.shiftKey) {
window.open(suggestion.url, '_blank');
} else if (event.altKey) {
// Keep native browser behavior
} else {
window.location.assign(suggestion.url);
}
}
},
});
(params: { query: string, props: AutocompleteOptions, state: AutocompleteState, ...setters }) => void | Promise <any>
Called when the input changes.
This turns experience in "controlled" mode. You'll be in charge of updating the state.
(params: { state: AutocompleteState }) => boolean
| defaults to({ state }) => state.suggestions.some(suggestion => suggestion.items.length > 0)
Called to check whether the dropdown should open based on the Autocomplete state.
The default behavior is to open the dropdown when there are results.
An Autocomplete source refers to an object with the following properties:
(params: { suggestion: Suggestion, state: AutocompleteState }) => string
| defaults to({ state }) => state.query
Called to get the value of the suggestion. The value is used to fill the search box.
If you do not wish to update the input value when an item is selected, you can return state.query
.
Example
const items = [{ value: 'Apple' }, { value: 'Banana' }];
const source = {
getInputValue: ({ suggestion }) => suggestion.value,
// ...
};
(params: { suggestion: Suggestion, state: AutocompleteState }) => string | undefined
Called to get the URL of the suggestion. The value is used to add keyboard accessibility features to allow to open suggestions in the current tab, in a new tab or in a new window.
Example
const items = [
{ value: 'Google', url: 'https://google.com' },
{ value: 'Amazon', url: 'https://amazon.com' },
];
const source = {
getSuggestionUrl: ({ suggestion }) => suggestion.url,
// ...
};
(params: { query: string, state: AutocompleteState, ...setters }) => Suggestion[] | Promise<Suggestion[]>
| required
Called when the input changes. You can use this function to filter/search the items based on the query.
Example
const items = [{ value: 'Apple' }, { value: 'Banana' }];
const source = {
getSuggestions({ query }) {
return items.filter((item) => item.value.includes(query));
},
// ...
};
required
Templates to use for the source. A template supports strings and JSX elements.
(params: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template to display before the suggestions.
(params: { suggestion: Suggestion, state: AutocompleteState, ...setters }) => string | JSX.Element
The template for each suggestion.
(params: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template to display after the suggestions.
(params: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template to display when there are no suggestions.
Example
Using strings
const items = [{ value: 'Apple' }, { value: 'Banana' }];
const source = {
templates: {
header() {
return '<h2>Fruits</h2>';
},
suggestion({ suggestion }) {
return suggestion.value;
},
footer() {
return '<a href="/fruits">See more</a>';
},
},
// ...
};
Using JSX elements
const items = [{ value: 'Apple' }, { value: 'Banana' }];
const source = {
templates: {
header() {
return <h2>Fruits</h2>;
},
suggestion({ suggestion }) {
return suggestion.value;
},
footer() {
return <a href="/fruits">See more</a>;
},
},
// ...
};
(params: { state: AutocompleteState, ...setters }) => void
| defaults to({ setIsOpen }) => setIsOpen(false)
Called when an item is selected.
ClassNames
CSS classes to add to the template of the source.
Example
const source = {
classNames: {
root: 'dropdown',
list: 'dropdown-menu',
suggestion: 'dropdown-item',
},
// ...
};
string
The CSS class to add to the source root.
string
The CSS class to add to the source list.
string
The CSS class to add to each source suggestion.
string
The CSS class to add to the source header.
string
The CSS class to add to the source footer.
string
The CSS class to add to the empty source.
The Autocomplete.js state drives the behavior of the experience.
The state can be initially set with initialState
and it's is passed to all templates.
string
| defaults to''
The query.
Suggestion[]
| defaults to[]
The suggestion of all the sources.
Suggestion
definition
interface Suggestion<TItem> {
source: AutocompleteSource;
items: TItem[];
}
boolean
| defaults tofalse
Whether the dropdown is open.
'idle' | 'loading' | 'stalled' | 'error'
| defaults toidle
The status of the autocomplete experience.
object
| defaults to{}
The autocomplete context to store data in. It's useful to use custom data in templates.
Each state has a setter that can be used in the lifecycle of Autocomplete.js.
(value: string) => void
Sets the query
value in the state.
(value: Suggestion[]) => void
Sets the suggestions
value in the state.
(value: boolean) => void
Sets the isOpen
value in the state.
(value: 'idle' | 'loading' | 'stalled' | 'error') => void
Sets the status
value in the state.
(value: object) => void
Sets the context
value in the state.
Example
Storing nbHits
from the Algolia response
autocomplete({
// ...
getSources({ query, setContext }) {
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'instant_search',
query,
params: {
attributesToSnippet: ['description'],
},
},
],
}).then((results) => {
const productsResults = results[0];
setContext({
nbProducts: productsResults.nbHits,
});
return [
{
// ...
templates: {
header({ state }) {
return `<h2>Products (${state.context.nbProducts})</h2>`;
},
},
},
];
});
},
});
In addition to the source templates, Autocomplete.js supports some global templates.
(params: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template to display before all sources.
(params: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template to display after all sources.
(params: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template for the submit icon.
(params: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template for the reset icon. The template for the submit icon.
(params: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template for the loading icon.
autocomplete
is the default export from the autocomplete.js
package. It is the main function that starts the autocomplete experience and accepts options.
The autocomplete
function returns an API that allows you to turn Autocomplete.js in a "controlled" mode. It returns all the setters so that you update the state of the experience.
// Instantiate Autocomplete.js
const autocompleteSearch = autocomplete({
// options
});
// Retrieve the state of your app that you want to forward to Autocomplete.js
const app = getAppState();
// Update the state of Autocomplete.js based on your app state
autocompleteSearch.setQuery(app.query);
autocompleteSearch.setSuggestions(
app.indices.map((index) => {
return {
source: getSource({ index }),
items: index.hits,
};
})
);
autocompleteSearch.setIsOpen(app.isOpen);
autocompleteSearch.setIsLoading(app.isLoading);
autocompleteSearch.setIsStalled(app.isStalled);
autocompleteSearch.setContext(app.context);
(params: { searchClient: SearchClient, query: string, searchParameters: SearchParameters[] }) => Promise<Response['hits']>
Function that retrieves and merges Algolia hits from multiple indices.
This function comes with default Algolia search parameters:
hitsPerPage
:5
highlightPreTag
:<mark>
highlightPostTag
:</mark>
Example
import autocomplete, { getAlgoliaHits } from 'autocomplete.js';
import algoliasearch from 'algoliasearch';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
autocomplete({
// ...
getSources({ query }) {
return [
{
// ...
getSuggestions({ query }) {
return getAlgoliaHits({
searchClient,
queries: [
{
indexName: 'instant_search',
query,
params: {
hitsPerPage: 3,
},
},
],
});
},
},
];
},
});
(params: { searchClient: SearchClient, query: string, searchParameters: SearchParameters[] }) => Promise<MultiResponse['results']>
Function that retrieves Algolia results from multiple indices.
This function comes with default Algolia search parameters:
hitsPerPage
:5
highlightPreTag
:<mark>
highlightPostTag
:</mark>
Example
import autocomplete, { getAlgoliaResults } from 'autocomplete.js';
import algoliasearch from 'algoliasearch';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
autocomplete({
// ...
getSources({ query }) {
return [
{
// ...
getSuggestions({ query }) {
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'instant_search',
query,
params: {
hitsPerPage: 3,
},
},
],
}).then((results) => {
const firstResult = results[0];
return firstResult.hits;
});
},
},
];
},
});
Highlights and escapes the value of a record.
Example
import autocomplete, { highlightAlgoliaHit } from 'autocomplete.js';
autocomplete({
// ...
templates: {
suggestion({ suggestion }) {
return highlightAlgoliaHit({
hit: suggestion,
attribute: 'name',
});
},
},
});
This function reverse-highlights and escapes the value of a record.
It's useful when following the pattern of Query Suggestions to highlight the difference between what the user types and the suggestion shown.
Example
import autocomplete, { reverseHighlightAlgoliaHit } from 'autocomplete.js';
autocomplete({
// ...
templates: {
suggestion({ suggestion }) {
return reverseHighlightAlgoliaHit({
hit: suggestion,
attribute: 'query',
});
},
},
});
Highlights and escapes the snippet value of a record.
Example
import autocomplete, { snippetAlgoliaHit } from 'autocomplete.js';
autocomplete({
// ...
templates: {
suggestion({ suggestion }) {
return snippetAlgoliaHit({
hit: suggestion,
attribute: 'name',
});
},
},
});
HTML output
<div
class="algolia-autocomplete"
role="combobox"
aria-haspopup="listbox"
aria-labelledby="autocomplete-0-label"
>
<form role="search" novalidate="" class="algolia-autocomplete-form">
<label
for="autocomplete-0-input"
class="algolia-autocomplete-magnifierLabel"
>
<svg>
...
</svg>
</label>
<div class="algolia-autocomplete-loadingIndicator">
<svg>
...
</svg>
</div>
<div class="algolia-autocomplete-searchbox">
<input
id="autocomplete-0-input"
class="algolia-autocomplete-input"
aria-autocomplete="list"
aria-labelledby="autocomplete-0-label"
autocomplete="off"
placeholder="Search…"
type="search"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
maxlength="512"
/>
</div>
<button
type="reset"
title="Clear the query"
class="algolia-autocomplete-reset"
hidden="true"
>
<svg>
...
</svg>
</button>
</form>
</div>
HTML output
<div class="algolia-autocomplete-dropdown">
<div class="algolia-autocomplete-dropdown-container">
<header class="algolia-autocomplete-header">
Global header
</header>
<section class="algolia-autocomplete-suggestions">
<header class="algolia-autocomplete-suggestions-header">
<h2>Fruits</h2>
</header>
<ul
id="autocomplete-0-menu"
role="listbox"
aria-labelledby="autocomplete-0-label"
>
<li
class="algolia-autocomplete-suggestions-item"
id="autocomplete-0-item-0"
role="option"
tabindex="0"
>
Apple
</li>
<li
class="algolia-autocomplete-suggestions-item"
id="autocomplete-0-item-1"
role="option"
tabindex="0"
>
Banana
</li>
</ul>
<footer class="algolia-autocomplete-suggestions-footer">
Showing 2 out of 10 fruits
</footer>
</section>
<footer class="algolia-autocomplete-footer">
Global footer
</footer>
</div>
</div>
Please refer to the contributing guide.
Autocomplete.js is MIT licensed.