Skip to main content
Widgets are the building blocks of InstantSearch. They connect UI components to the search state, handle user interactions, and render search results.

What is a widget?

A widget is an object that implements specific lifecycle methods to interact with the InstantSearch instance. Widgets can:
  • Read and modify search parameters
  • Render UI based on search results
  • Handle user interactions and update search state
  • Manage their own internal state
Every widget implements the Widget interface defined in /home/daytona/workspace/source/packages/instantsearch.js/src/types/widget.ts:337-348.

Widget lifecycle

Widgets go through several lifecycle phases:
1

Initialization

The init() method is called once before the first search. This is where you set up event listeners and render the initial UI.
init(options: InitOptions) {
  const { helper, instantSearchInstance } = options;
  // Initial setup and rendering
}
2

Search parameters

The getWidgetSearchParameters() method is called to collect search parameters from all widgets before making a search request.
getWidgetSearchParameters(state: SearchParameters, { uiState }) {
  return state.setQueryParameter('query', uiState.query || '');
}
3

Rendering

The render() method is called after each search response with the results.
render(options: RenderOptions) {
  const { results, instantSearchInstance } = options;
  // Update UI with results
}
4

Disposal

The dispose() method is called when the widget is removed, allowing it to clean up and remove its search parameters.
dispose({ state }) {
  // Clean up event listeners
  return state.setQueryParameter('query', undefined);
}

Widget interface

The complete widget interface includes these methods (from /home/daytona/workspace/source/packages/instantsearch.js/src/types/widget.ts):

Type identification

$$type
string
required
Identifier for the widget type, typically in the format 'ais.widgetName'.
$$type: 'ais.searchBox'
$$widgetType
string
Optional identifier for UI widgets (as opposed to connectors).

Lifecycle methods

init
(options: InitOptions) => void
Called once before the first search. Use this to:
  • Set up event listeners
  • Render initial UI
  • Initialize internal state
The InitOptions include:
  • instantSearchInstance: The InstantSearch instance
  • parent: The parent index widget
  • helper: The Algolia helper
  • state: Current search parameters
  • uiState: Current UI state
render
(options: RenderOptions) => void
Called after each search response. Use this to update the UI with new results.The RenderOptions include everything from InitOptions plus:
  • results: Search results from Algolia
  • scopedResults: Results for all indices
dispose
(options: DisposeOptions) => SearchParameters | void
Called when the widget is removed. Return updated search parameters with your widget’s parameters removed.

State management

getWidgetSearchParameters
function
Converts UI state to search parameters. Called before each search.
getWidgetSearchParameters(
  state: SearchParameters,
  { uiState }
): SearchParameters {
  return state.setQueryParameter('query', uiState.query || '');
}
getWidgetUiState
function
Converts search parameters to UI state. Used for routing and state synchronization.
getWidgetUiState(
  uiState: IndexUiState,
  { searchParameters }
): IndexUiState {
  const query = searchParameters.query || '';
  if (query === '') return uiState;
  return { ...uiState, query };
}

Render state (optional)

getRenderState
function
Returns the complete render state for the index, including this widget’s state.
getRenderState(renderState: IndexRenderState, renderOptions) {
  return {
    ...renderState,
    searchBox: this.getWidgetRenderState(renderOptions),
  };
}
getWidgetRenderState
function
Returns just this widget’s render state.
getWidgetRenderState(renderOptions) {
  return {
    query: renderOptions.state.query || '',
    refine: (value: string) => helper.setQuery(value).search(),
    widgetParams,
  };
}

Built-in widgets

InstantSearch provides many built-in widgets. Here are the most common ones (from /home/daytona/workspace/source/packages/instantsearch.js/src/types/widget.ts:99-137):
  • ais.searchBox - Search input
  • ais.hits - Display search results
  • ais.infiniteHits - Infinite scrolling results
  • ais.pagination - Page navigation
  • ais.stats - Search statistics
  • ais.refinementList - Faceted filters
  • ais.menu - Single-selection facets
  • ais.hierarchicalMenu - Hierarchical categories
  • ais.rangeSlider - Numeric range filter
  • ais.ratingMenu - Rating filter
  • ais.toggleRefinement - Boolean filter
  • ais.configure - Set search parameters
  • ais.index - Multi-index search
  • ais.dynamicWidgets - Dynamic facets
  • ais.relatedProducts - Related items
  • ais.frequentlyBoughtTogether - Product bundles
  • ais.trendingItems - Trending content
  • ais.lookingSimilar - Similar items

Using widgets

import instantsearch from 'instantsearch.js';
import { searchBox, hits, pagination } from 'instantsearch.js/es/widgets';

const search = instantsearch({
  indexName: 'products',
  searchClient,
});

search.addWidgets([
  searchBox({
    container: '#searchbox',
    placeholder: 'Search products...',
  }),
  hits({
    container: '#hits',
    templates: {
      item: (hit) => `<h2>${hit.name}</h2>`,
    },
  }),
  pagination({
    container: '#pagination',
  }),
]);

search.start();

Creating custom widgets

You can create custom widgets by implementing the widget interface:
function customWidget(widgetParams) {
  return {
    $$type: 'custom.myWidget',
    
    init({ helper, instantSearchInstance }) {
      // Set up your widget
      const container = document.querySelector(widgetParams.container);
      
      container.addEventListener('click', () => {
        // Handle user interaction
        helper.setQuery('new query').search();
      });
    },
    
    render({ results }) {
      // Update UI with results
      const container = document.querySelector(widgetParams.container);
      container.innerHTML = `Found ${results.nbHits} results`;
    },
    
    dispose() {
      // Clean up
      const container = document.querySelector(widgetParams.container);
      container.innerHTML = '';
    },
  };
}

// Use it
search.addWidgets([
  customWidget({ container: '#my-widget' })
]);
For more complex custom widgets, consider using connectors to separate business logic from rendering.

Widget search parameters

Widgets can modify search parameters through the getWidgetSearchParameters method. Common parameters include:
  • query - Search query
  • facets, disjunctiveFacets, hierarchicalFacets - Facet configuration
  • facetFilters, numericFilters - Active filters
  • hitsPerPage - Results per page
  • page - Current page
  • aroundLatLng, aroundRadius - Geo search
Example from SearchBox connector (from /home/daytona/workspace/source/packages/instantsearch.js/src/connectors/search-box/connectSearchBox.ts:169-171):
getWidgetSearchParameters(searchParameters, { uiState }) {
  return searchParameters.setQueryParameter('query', uiState.query || '');
}

Widget dependencies

Widgets can depend on either search or recommend APIs:
type SearchWidget = {
  dependsOn?: 'search';
  getWidgetParameters: (state: SearchParameters) => SearchParameters;
};

type RecommendWidget = {
  dependsOn: 'recommend';
  getWidgetParameters: (state: RecommendParameters) => RecommendParameters;
};
Recommendation widgets like relatedProducts and frequentlyBoughtTogether use the recommend API.

Best practices

Keep widgets focused

Each widget should have a single responsibility. Use multiple widgets for complex UIs.

Clean up resources

Always implement dispose() to remove event listeners and clean up DOM elements.

Use connectors for reusability

Separate business logic with connectors when building custom widgets for multiple frameworks.

Leverage render state

Implement getRenderState and getWidgetRenderState for framework integrations and debugging.

Connectors

Separate business logic from rendering

InstantSearch instance

Learn about the main instance

Search state

Understanding UI state management

Widget API reference

Complete widget documentation

Build docs developers (and LLMs) love