The @appbaseio/autocomplete-suggestions-plugin is framework agnostic, similar to autocomplete-js. In this module, we explore how we can use our plugin to power our search-input ui in various ways.

Use with React

This guide shows how to create a React Autocomplete component. It uses the useEffect hook to create and mount the component. We pass our @appbaseio/autocomplete-suggestions-plugin to act as a suggestion source.

Creating the component Start with some boilerplate for creating a React component. This component uses the useRef hook to create a mutable ref object, containerRef, to mount the autocomplete on. To learn more about this hook, check out the useRef React documentation.

All that you need to render is a div with the id as autocomplete.

Copy
import React, { useEffect } from 'react';
import ReactDOM from "react-dom";

import { autocomplete } from "@algolia/autocomplete-js";
import "@algolia/autocomplete-theme-classic";
import createSuggestionsPlugin from "@appbaseio/autocomplete-suggestions-plugin";

// scaffolding described for autocomplete goes here
// see below snippet

function Autocomplete(props) {

  useEffect(() => {
    // initiate autocomplete-js
    // see definition in below snippet
    initAutocomplete();

    // cleanup before mounting
    return () => {
      autocomplete.destroy();
    };
  }, []);

  return <div id="autocomplete"></div>;
}

const rootElement = document.getElementById("root");

ReactDOM.render(
  <StrictMode>
    <Autocomplete />
  </StrictMode>,
  rootElement
);

Let's look at the initAutocomplete() function and other scaffolding for the plugin.

Copy
// appbase client config object
const appbaseClientConfig = {
  url: "https://appbase-demo-ansible-abxiydt-arc.searchbase.io",
  app: "best-buy-dataset",
  credentials: "b8917d239a52:82a2f609-6439-4253-a542-3697f5545947",
  settings: { userId: "s@s", recordAnalytics: true },
};

const rsApiConfig = {
  highlight: true,
  dataField: [
    {
      field: "name.autosuggest",
      weight: 1,
    },
    {
      field: "name",
      weight: 3,
    },
  ],
  enableRecentSuggestions: true,
  recentSuggestionsConfig: {
    size: 2,
    minHits: 2,
    minChars: 4,
    index: "best-buy-dataset",
  },
  enablePopularSuggestions: true,
  popularSuggestionsConfig: {
    size: 2,
    minChars: 3,
    minCount: 3,
    index: "best-buy-dataset",
  },
  index: "best-buy-dataset",
  size: 5,
};

// default usage: plugin to fetch suggestions
const defaultUsagePlugin = createSuggestionsPlugin(appbaseClientConfig, {
  ...rsApiConfig
});

// initiator for  autocomplete-js
const initAutocomplete = () => {
  autocomplete({
    container: "#autocomplete",
    placeholder: "Search for products",
    openOnFocus: true,
    debug: true,
    plugins: [defaultUsagePlugin]
    // use the below code incase trying to render
    // custom jsx using templates
    // renderer: { createElement, Fragment },
    // render({ children }, root) {
    //   render(children, root);
    // },
  });
};

Use with Vue

This guide shows how to integrate an Autocomplete instance into a Vue application. It uses the Vue’s Composition API, specifically you can instantiate an Autocomplete instance in the onMounted lifecycle hook in the setup function.. We pass our @appbaseio/autocomplete-suggestions-plugin to act as a suggestion source.

Mounting the autocomplete You can instantiate and mount your Autocomplete instance in the onMounted lifecycle hook in the setup function. Doing so requires passing the renderer and render parameters.

This is because the default Autocomplete implementation uses Preact’s version of createElement, Fragment and render. Without providing Vue’s version of these, the Autocomplete instance won’t render the views properly.

Copy
import { onMounted } from "vue";
import { autocomplete } from "@algolia/autocomplete-js";

import "@algolia/autocomplete-theme-classic";

import { createElement } from "./adapter";
import createSuggestionsPlugin from "@appbaseio/autocomplete-suggestions-plugin";

// appbase client config object
const appbaseClientConfig = {
  url: "https://appbase-demo-ansible-abxiydt-arc.searchbase.io",
  app: "best-buy-dataset",
  credentials: "b8917d239a52:82a2f609-6439-4253-a542-3697f5545947",
  settings: { userId: "s@s", recordAnalytics: true },
};

// reactivesearch api configuration
const rsApiConfig = {
  highlight: true,
  dataField: [
    {
      field: "name.autosuggest",
      weight: 1,
    },
    {
      field: "name",
      weight: 3,
    },
  ],
  enableRecentSuggestions: true,
  recentSuggestionsConfig: {
    size: 2,
    minHits: 2,
    minChars: 4,
    index: "best-buy-dataset",
  },
  enablePopularSuggestions: true,
  popularSuggestionsConfig: {
    size: 2,
    minChars: 3,
    minCount: 3,
    index: "best-buy-dataset",
  },
  index: "best-buy-dataset",
  size: 5,
};

// default usage: plugin to fetch suggestions
const defaultUsagePlugin = createSuggestionsPlugin(appbaseClientConfig, {
  ...rsApiConfig
});

export default {
  name: "App",
  setup() {
    onMounted(() => {
      autocomplete({
        container: "#autocomplete",
        placeholder: "Search",
        openOnFocus: true,
        plugins: [defaultUsagePlugin],
        detachedMediaQuery: "none"
      });
    });
  },
  render() {
    const style = {
      margin: "0 auto",
      "max-width": "640px",
      width: "100%"
    };

    return createElement(
      "div",
      { style },
      createElement("div", { id: "autocomplete" })
    );
  }
};

Let's have a look at the adapter.js. We are using this adapter to custom render our elements and transforming the props so that they are compatible with Vue3.

Copy
import { h } from "vue";

export function createElement(type, props, ...children) {
  const adaptedProps = Object.entries(props || {}).reduce(
    (acc, [key, value]) => {
      // Vue 3 accepts lower-case event names so we need to transform props like
      // `onMouseMove` to `onMousemove`.
      const property =
        key[0] === "o" && key[1] === "n"
          ? key.slice(0, 3) + key.slice(3).toLowerCase()
          : key;

      acc[property] = value;
      return acc;
    },
    {}
  );

  return h(type, adaptedProps, ...children);
}

Providing suggestions helps users type longer queries, a good suggestion ensures the best results. It is easy to build an auto-complete experience with appbase.io but popular suggestions allows you to display more specific and popular query suggestions.

To get started, refer to the basic example and then follow along.

To enable Popular searches, we enable the enablePopularSuggestions by setting it to true. Further, we tweak our popular suggestions' config using popularSuggestionsConfig.

Copy
import { autocomplete } from "@algolia/autocomplete-js";
import "@algolia/autocomplete-theme-classic";
import createSuggestionsPlugin from "@appbaseio/autocomplete-suggestions-plugin";


// appbase client config object
const appbaseClientConfig = {
  url: "https://appbase-demo-ansible-abxiydt-arc.searchbase.io",
  app: "best-buy-dataset",
  credentials: "b8917d239a52:82a2f609-6439-4253-a542-3697f5545947",
  settings: {
    userId: "s@s",
    recordAnalytics: true,
  },
};

// reactivesearch api configuration
const rsApiConfig = {
  dataField: [
    {
      field: "name.autosuggest",
      weight: 1,
    },
    {
      field: "name",
      weight: 3,
    },
  ],
  enablePopularSuggestions: true,
  popularSuggestionsConfig: {
    size: 2,
    minChars: 3,
    minCount: 3,
    index: "best-buy-dataset",
  },
  index: "best-buy-dataset",
  size: 5,
};


// instantiatin the plugin
const suggestionsPlugin = createSuggestionsPlugin(appbaseClientConfig, {
  ...rsApiConfig,
});

autocomplete({
  container: '#autocomplete',
  plugins: [suggestionsPlugin],
  openOnFocus: true,
});

Adding Recent Searches

Providing suggestions helps users type longer queries, a good suggestion ensures the best results. It is easy to build an auto-complete experience with appbase.io but recent suggestions allows you to display more contextual and recent query suggestions.

To get started, refer to the basic example and then follow along.

To enable Recent searches, we enable the enableRecentSuggestions by setting it to true. Further, we tweak our popular suggestions' config using recentSuggestionsConfig.

Copy
import { autocomplete } from "@algolia/autocomplete-js";
import "@algolia/autocomplete-theme-classic";
import createSuggestionsPlugin from "@appbaseio/autocomplete-suggestions-plugin";


// appbase client config object
const appbaseClientConfig = {
  url: "https://appbase-demo-ansible-abxiydt-arc.searchbase.io",
  app: "best-buy-dataset",
  credentials: "b8917d239a52:82a2f609-6439-4253-a542-3697f5545947",
  settings: {
    userId: "s@s",
    recordAnalytics: true,
  },
};

// reactivesearch api configuration
const rsApiConfig = {
  dataField: [
    {
      field: "name.autosuggest",
      weight: 1,
    },
    {
      field: "name",
      weight: 3,
    },
  ],
  enableRecentSuggestions: true,
  recentSuggestionsConfig: {
    size: 2,
    minHits: 2,
    minChars: 4,
    index: "best-buy-dataset",
  },
  index: "best-buy-dataset",
  size: 5,
};

// instantiatin the plugin
const suggestionsPlugin = createSuggestionsPlugin(appbaseClientConfig, {
  ...rsApiConfig,
});

autocomplete({
  container: '#autocomplete',
  plugins: [suggestionsPlugin],
  openOnFocus: true,
});

Adding Multiple Result Types

While @appbaseio/autocomplete-suggestions-plugin with suggested index searches is a ubiquitous search experience, providing with index, recent, popular, and promoted types of suggestions, rich multi-category autocompletes are becoming more and more popular.

For example, if you search for something on an ecommerce site, your results could contain not just text suggestions, but also categories wthey belong to, preview images, ratings and more. Ecommerce stores often show suggested searches, products, blog posts, brands, and categories all in one autocomplete.

It’s best to display different results types in different sections. This implicitly gives users a better understanding of what the items are, and what will happen if they select an item.

To get started, refer to the basic example and then follow along.

Let's start with enabling all types of suggestions, namely, index(enabled by-default), recent, and popular suggestions.

Copy
// reactivesearch api configuration
const rsApiConfig = {
  highlight: true,
  dataField: [
    {
      field: "name.autosuggest",
      weight: "1",
    },
    {
      field: "name",
      weight: "3",
    },
  ],
  enableRecentSuggestions: true,
  recentSuggestionsConfig: {
    size: 2,
    minHits: 2,
    minChars: 4,
    index: "best-buy-dataset",
  },
  enablePopularSuggestions: true,
  popularSuggestionsConfig: {
    size: 2,
    minChars: 3,
    minCount: 3,
    index: "best-buy-dataset",
  },
  index: "best-buy-dataset",
  size: 5,
};
Copy
/** @jsx h */
import { autocomplete } from "@algolia/autocomplete-js";
import "@algolia/autocomplete-theme-classic";
import createSuggestionsPlugin from "@appbaseio/autocomplete-suggestions-plugin";
import { h } from "preact";
import { renderResults } from "./utils";

var JSONTreeView = require("json-tree-view");
// appbase client config object
const appbaseClientConfig = {
  url: "https://appbase-demo-ansible-abxiydt-arc.searchbase.io",
  app: "best-buy-dataset",
  credentials: "b8917d239a52:82a2f609-6439-4253-a542-3697f5545947",
  settings: {
    userId: "s@s",
    enableQueryRules: true,
    recordAnalytics: true,
  },
};

// reactivesearch api configuration
const rsApiConfig = {
  /// described above
};

// instantiating the plugin
// default usage: plugin to fetch suggestions
const defaultUsagePlugin = createSuggestionsPlugin(
  appbaseClientConfig,
  {
    ...rsApiConfig,
  },
  {
    onItemSelect: (props) => {
      const {
        item: { label },
        setQuery,
      } = props;

      setQuery(label.replace(/(<([^>]+)>)/gi, ""));
      renderResults(
        {
          value: label.replace(/(<([^>]+)>)/gi, ""),
          url: appbaseClientConfig.url,
          app: appbaseClientConfig.app,
          credentials: appbaseClientConfig.credentials,
          settings: appbaseClientConfig.settings,
          query: {
            dataField: rsApiConfig.dataField,
          },
        },
        JSONTreeView
      );
    },
  }
);

autocomplete({
  container: '#autocomplete',
  plugins: [defaultUsagePlugin],
  openOnFocus: true,
});

Now, let's create one more instance of @appbaseio/autocomplete-suggestions-plugin plugin that would render a second section displaying the products with hyperlink attached to each suggestion item.

Copy
// advanced usage: plugin to fetch suggestions and
// render custom ui for header, footer and suggestion items
const advancedUsagePlugin = createSuggestionsPlugin(
  {
    ...appbaseClientConfig,
    settings: {
      ...appbaseClientConfig.settings,
      enableQueryRules: false,
    },
  },
  {
    ...rsApiConfig,
    enableRecentSuggestions: false,
    enablePopularSuggestions: false,
  },
  {
    renderItem: (props) => {
      const { item } = props;
      return (
        <a
          className="aa-item product-item"
          href={
            item._source
              ? item._source.url
              : `https://www.google.com/search?q=${item.value}`
          }
          target="_blank"
          rel="noreferrer"
        >
          <div className="product-image">
            <img
              src={
                item._source
                  ? item._source.image
                  : "https://m.media-amazon.com/images/I/81c83vd4O+L._SY879_.jpg"
              }
              alt={item.value}
            />
          </div>
          <div className="product-details">
            <h4>{item.value} (Promoted)</h4>
            <p>
              {item._source
                ? item._source.shortDescription
                : "Samsung offers latest smartphones with advanced technology and design. Buy 3G, 4G, dual sim smartphone at best prices in India."}
            </p>
          </div>
        </a>
      );
    },
    onItemSelect: (props) => {
      const {
        item: { url, label, type },
        setQuery,
        refresh,
      } = props;

      if (url) {
        window.open(url);
      } else if (type !== "index") {
        setQuery(label.replace(/(<([^>]+)>)/gi, ""));
        renderResults(
          {
            value: label.replace(/(<([^>]+)>)/gi, ""),
            url: appbaseClientConfig.url,
            app: appbaseClientConfig.app,
            credentials: appbaseClientConfig.credentials,
            settings: appbaseClientConfig.settings,
            query: {
              dataField: rsApiConfig.dataField,
            },
          },
          JSONTreeView
        );
        refresh();
      }
    },
    renderHeader: (props) => {
      return (
        <h4>
          Products Listing <hr style={{ borderColor: "#d7d5f5" }} />
        </h4>
      );
    },
    renderFooter: (props) => {
      return <hr style={{ borderColor: "#d7d5f5" }} />;
    },
    renderNoResults: (props) => {
      if (props.state.query === "") {
        return <p>Search for something to get direct product suggestions!</p>;
      } else {
        return <p>No products found! Try searching for something else!</p>;
      }
    },
  }
);

Finally, the autocomplete function looks like,

Copy
autocomplete({
  container: "#autocomplete",
  placeholder: "Search for products",
  openOnFocus: true,
  debug: true, //uncomment to keep the dropdown open
  plugins: [defaultUsagePlugin, advancedUsagePlugin],
  detachedMediaQuery: "none",
});

Sending Appbase Analytics Insights

Analytics Insights provides tips and tricks for your business based on the recent activities of your business. Apbbase.io tracks the search activity of your users and discovers the important changes in your analytics data and from that, we prepare a list of insights with the recommendations so you can take meaningful action to improve your search experience.

Refer to our comprehensive guide to implement analytics here.

Since our plugin is using Appbase indices as sources for suggestions, Appbase provides Search Analytics out-of-the-box. Search Analytics includes metrics like top searches, No Results Searches, Low Clicks, Low Suggestions Clicks, Higher Click Position, etc.

To enable analytics, we enable the recordAnalytics by setting it to true, passed in the settings object in the appbaseClientConfig.

Copy
import { autocomplete } from "@algolia/autocomplete-js";
import "@algolia/autocomplete-theme-classic";
import createSuggestionsPlugin from "@appbaseio/autocomplete-suggestions-plugin";
import { renderResults } from "./utils";

var JSONTreeView = require("json-tree-view");
// appbase client config object
const appbaseClientConfig = {
  url: "https://appbase-demo-ansible-abxiydt-arc.searchbase.io",
  app: "best-buy-dataset",
  credentials: "b8917d239a52:82a2f609-6439-4253-a542-3697f5545947",
  settings: {
    userId: "s@s",
    enableQueryRules: true,
    recordAnalytics: true,
    customEvents: {
      platform: "web",
    },
  },
};

// reactivesearch api configuration
const rsApiConfig = {
  size: 5,
  highlight: true,
  dataField: [
    {
      field: "name.autosuggest",
      weight: "1",
    },
    {
      field: "name",
      weight: "3",
    },
  ],
  enableRecentSuggestions: true,
  recentSuggestionsConfig: {
    size: 2,
    minHits: 2,
    minChars: 4,
    index: "best-buy-dataset",
  },
  enablePopularSuggestions: true,
  popularSuggestionsConfig: {
    size: 2,
    minChars: 3,
    minCount: 3,
    index: "best-buy-dataset",
  },
  index: "best-buy-dataset",
};

// instantiating the plugin
const suggestionsPlugin = createSuggestionsPlugin(
  appbaseClientConfig,
  {
    ...rsApiConfig,
  },
  {
    onItemSelect: (props) => {
      const {
        item: { label },
        setQuery,
      } = props;

      setQuery(label.replace(/(<([^>]+)>)/gi, ""));
      renderResults(
        {
          value: label.replace(/(<([^>]+)>)/gi, ""),
          url: appbaseClientConfig.url,
          app: appbaseClientConfig.app,
          credentials: appbaseClientConfig.credentials,
          settings: appbaseClientConfig.settings,
          query: {
            dataField: rsApiConfig.dataField,
          },
        },
        JSONTreeView
      );
    },
  }
);

autocomplete({
  container: "#autocomplete",
  plugins: [suggestionsPlugin],
  debug: false,
  openOnFocus: true,
  detachedMediaQuery: "none",
  // ...
});

Creating A Custom Renderer

The autocomplete-js package includes everything you need to render a JavaScript autocomplete experience that you can bind to your own framework. If you want to build a custom UI that differs from the autocomplete-js output, for example in React or another front-end framework, the autocomplete-core package provides all the primitives to build it.

The guide here shows how to leverage all the autocomplete capacities to build an accessible autocomplete, both for desktop and mobile, with React. You can find the final result in this sandbox.

Changing Behavior Based on the Query

You may want to change which sources you use depending on the query. A typical pattern is to display a different source when the query is empty and switch once the user starts typing.

This tutorial explains how to show static predefined items when the query is empty, and results from an Algolia index when it isn’t.

Checkout the guide here.

Reshaping Sources

When you’re browsing a website that fetches content from a database, the UI isn’t fully representative of how that data is structured on the back-end. This allows more human-friendly experiences and interactions. A search UI doesn’t have to be a one-to-one mapping with your search engine either.

The Autocomplete Reshape API lets you transform static, dynamic and asynchronous sources into friendlier search UIs.

Here are some examples of what you can do with the Reshape API:

  • Apply a limit of items for a group of sources
  • Remove duplicates in a group of sources
  • Group sources by an attribute
  • Sort sources

Checkout the guide here.