UI Libraries / Autocomplete / Plugins

An autocomplete can be much more than a functional combo box. Autocomplete lets you extend and encapsulate custom behavior with its Plugin API.

For example, the official Algolia Insights plugin automatically sends click and conversion events to the Algolia Insights API whenever a user interacts with the autocomplete.

You can use one of the existing official plugins or build your own.

Using an Autocomplete plugin

When using an Autocomplete plugin, all you need to do is provide it via the plugins option.

For example, when using the Insights plugin, you can instantiate the plugin, then pass it down to your Autocomplete instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import algoliasearch from 'algoliasearch/lite';
import { autocomplete } from '@algolia/autocomplete-js';
import { createAlgoliaInsightsPlugin } from '@algolia/autocomplete-plugin-algolia-insights';
import insightsClient from 'search-insights';

const appId = 'latency';
const apiKey = '6be0576ff61c053d5f9a3225e2a90f76';
const searchClient = algoliasearch(appId, apiKey);
insightsClient('init', { appId, apiKey });

const algoliaInsightsPlugin = createAlgoliaInsightsPlugin({ insightsClient });

autocomplete({
  // ...
  plugins: [algoliaInsightsPlugin],
});

Plugins execute sequentially, in the order you define them.

Building your own plugin

An Autocomplete plugin is an object that implements the AutocompletePlugin interface.

It can provide sources, react to state changes, and hook into various autocomplete lifecycle steps. It has access to setters, including the Context API, allowing it to store and retrieve arbitrary data at any time.

The following example creates a plugin that searches into a static list of GitHub repositories.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const gitHubReposPlugin = {
  getSources() {
    return [
      {
        sourceId: 'githubPlugin',
        getItems({ query }) {
          return [
            { name: 'algolia/autocomplete', stars: 1237 },
            { name: 'algolia/algoliasearch-client-javascript', stars: 884 },
            { name: 'algolia/algoliasearch-client-php', stars: 554 },
          ].filter(({ name }) =>
            name.toLowerCase().includes(query.toLowerCase())
          );
        },
        getItemUrl({ item }) {
          return `https://github.com/${item.name}`;
        },
        templates: {
          item({ item }) {
            const stars = new Intl.NumberFormat('en-US').format(item.stars);

            return `${item.name} (${stars} stars)`;
          },
          noResults() {
            return 'No results.';
          },
        },
      },
    ];
  },
};

autocomplete({
  // ...
  plugins: [gitHubReposPlugin],
});

If you want to package and distribute your plugin for other people to use, you might want to expose a function instead. For example, you can use the GitHub API to search into all repositories, and let people pass API parameters as plugin options.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import qs from 'qs';

function debouncePromise(fn, time) {
  let timerId = undefined;

  return function (...args) {
    if (timerId) {
      clearTimeout(timerId);
    }

    return new Promise((resolve) => {
      timerId = setTimeout(() => resolve(fn(...args)), time);
    });
  };
}

const debouncedFetch = debouncePromise(fetch, 300);

const baseUrl = `https://api.github.com/search/repositories`;

export function createGitHubReposPlugin(options) {
  return {
    getSources({ query }) {
      const queryParameters = qs.stringify({ ...options, q: query });
      const endpoint = [baseUrl, queryParameters].join('?');

      return debouncedFetch(endpoint)
        .then((response) => response.json())
        .then((repositories) => {
          return [
            {
              sourceId: 'githubPlugin',
              getItems() {
                return repositories.items;
              },
              getItemUrl({ item }) {
                return item.html_url;
              },
              templates: {
                item({ item }) {
                  const stars = new Intl.NumberFormat('en-US').format(
                    item.stargazers_count
                  );

                  return `${item.full_name} (${stars} stars)`;
                },
                noResults() {
                  return 'No results.';
                },
              },
            },
          ];
        });
    },
  };
}
1
2
3
4
5
6
7
8
9
10
11
import { autocomplete } from '@algolia/autocomplete-js';
import { createGitHubReposPlugin } from './createGitHubReposPlugin';

const gitHubReposPlugin = createGitHubReposPlugin({
  per_page: 5,
});

autocomplete({
  container: '#autocomplete',
  plugins: [gitHubReposPlugin],
});

You can see this demo live on CodeSandbox.

The GitHub Search API is rate limited, which means you need to debounce calls to avoid 403 errors. For instant search results with no rate limiting, highlighted results, flexible custom ranking, and more, you can index repositories into Algolia instead.

Subscribing to source lifecycle hooks

When building, you also get access to the subscribe method. It runs once when the autocomplete instance starts and lets you subscribe to lifecycle hooks and interact with the instance’s state and context.

For example, imagine you want to build a plugin that sends events to Google Analytics when the user navigates results. You can use subscribe to hook into onSelect and onActive events and use the Google Analytics API there.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function createGoogleAnalyticsPlugin({ trackingId, options }) {
  return {
    subscribe({ onSelect, onActive }) {
      ga('create', trackingId, ...options);

      const event = {
        hitType: 'event',
        eventCategory: 'Autocomplete',
        eventLabel: item.name,
      };

      onSelect(({ item }) => {
        ga('send', {
          ...event,
          eventAction: 'select',
        });
      });

      onActive(({ item }) => {
        ga('send', {
          ...event,
          eventAction: 'active',
        });
      });
    },
  };
}

Official plugins

There are a few useful official plugins you can already use with Autocomplete.

Reference

name
type: string | undefined

A name to identify the plugin.

subscribe
type: (params: { onSelect: (fn: params: TParams) => void, onActive: (fn: params: TParams) => void, ...setters: AutocompleteSetters }) => void

The function called when Autocomplete starts.

onStateChange
type: (params: { state: AutocompleteState<TItem> }) => void

The function called when the internal state changes.

onSubmit
type: (params: { state: AutocompleteState, event: Event, ...setters: AutocompleteSetters }) => void

The function called when submitting the Autocomplete form.

onReset
type: (params: { state: AutocompleteState, event: Event, ...setters: AutocompleteSetters }) => void

The function called when resetting the Autocomplete form.

getSources
type: (params: { query: string, state: AutocompleteState, ...setters: AutocompleteSetters }) => Array<AutocompleteSource> | Promise<Array<AutocompleteSource>>

The sources to get the suggestions from.

When defined, they’re merged with the sources of your Autocomplete instance.

data
type: unknown

An extra plugin object to expose properties and functions as APIs.

Did you find this page helpful?