Skip to main content
Middlewares allow you to extend InstantSearch with custom behavior, analytics tracking, and third-party integrations. They hook into the search lifecycle to observe or modify the search flow.

Available Middlewares

createInsightsMiddleware

Integrate Algolia Insights for click and conversion analytics

createRouterMiddleware

Advanced routing customization

createMetadataMiddleware

Add metadata to search requests

createInsightsMiddleware

The Insights middleware integrates Algolia Insights to track user interactions and enable personalization features.

Usage

import { createInsightsMiddleware } from 'instantsearch.js/es/middlewares';
import instantsearch from 'instantsearch.js';

const insightsMiddleware = createInsightsMiddleware({
  insightsClient: window.aa,
});

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

search.use(insightsMiddleware);

Options

insightsClient
Function
The Algolia Insights client instance. If not provided, the middleware will look for window.aa.
import aa from 'search-insights';

createInsightsMiddleware({
  insightsClient: aa,
})
insightsInitParams
object
Configuration passed to aa('init', ...).
createInsightsMiddleware({
  insightsClient: window.aa,
  insightsInitParams: {
    useCookie: true,
    userToken: 'user-123',
  },
})
onEvent
Function
Callback function called before sending events to Insights.
createInsightsMiddleware({
  insightsClient: window.aa,
  onEvent: (event, insightsClient) => {
    console.log('Sending event:', event);
    // Optionally send to custom analytics
    insightsClient(event.insightsMethod, event.payload);
  },
})

Examples

Basic Insights Integration

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

// Load Insights library
!function(e,a,t,n,s,i,c){e.AlgoliaAnalyticsObject=s,e[s]=e[s]||function(){
(e[s].queue=e[s].queue||[]).push(arguments)},i=a.createElement(t),c=a.getElementsByTagName(t)[0],
i.async=1,i.src=n,c.parentNode.insertBefore(i,c)
}(window,document,"script","https://cdn.jsdelivr.net/npm/search-insights@2/dist/search-insights.min.js","aa");

const insightsMiddleware = createInsightsMiddleware({
  insightsClient: window.aa,
});

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

search.use(insightsMiddleware);

search.addWidgets([
  searchBox({ container: '#searchbox' }),
  hits({ container: '#hits' }),
]);

search.start();

Authenticated Users

import { createInsightsMiddleware } from 'instantsearch.js/es/middlewares';

const userId = getCurrentUserId(); // Your auth logic

const insightsMiddleware = createInsightsMiddleware({
  insightsClient: window.aa,
  insightsInitParams: {
    userToken: userId,
    useCookie: false, // Don't use cookies for authenticated users
  },
});

search.use(insightsMiddleware);

Custom Event Handling

import { createInsightsMiddleware } from 'instantsearch.js/es/middlewares';

const insightsMiddleware = createInsightsMiddleware({
  insightsClient: window.aa,
  onEvent: (event, insightsClient) => {
    // Send to Algolia Insights
    insightsClient(event.insightsMethod, event.payload);
    
    // Also send to Google Analytics
    if (event.insightsMethod === 'clickedObjectIDsAfterSearch') {
      gtag('event', 'select_item', {
        items: event.payload.objectIDs.map(id => ({ id })),
      });
    }
    
    // Or send to custom analytics
    fetch('/api/analytics', {
      method: 'POST',
      body: JSON.stringify(event),
    });
  },
});

search.use(insightsMiddleware);

Personalization Setup

import { createInsightsMiddleware } from 'instantsearch.js/es/middlewares';
import { configure } from 'instantsearch.js/es/widgets';

const insightsMiddleware = createInsightsMiddleware({
  insightsClient: window.aa,
  insightsInitParams: {
    useCookie: true,
  },
});

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

search.use(insightsMiddleware);

// Enable personalization
search.addWidgets([
  configure({
    enablePersonalization: true,
    userToken: 'user-123', // Synced automatically by middleware
  }),
]);

createRouterMiddleware

Advanced middleware for custom routing behavior.
import { createRouterMiddleware } from 'instantsearch.js/es/middlewares';

const routerMiddleware = createRouterMiddleware({
  router: customRouter,
  stateMapping: customStateMapping,
});

search.use(routerMiddleware);
Most use cases are covered by the routing option in instantsearch(). Use this middleware only for advanced customization.

createMetadataMiddleware

Add custom metadata to search requests.
import { createMetadataMiddleware } from 'instantsearch.js/es/middlewares';

const metadataMiddleware = createMetadataMiddleware({
  userAgents: [{
    name: 'my-app',
    version: '1.0.0',
  }],
});

search.use(metadataMiddleware);

Custom Middleware

You can create custom middlewares to hook into the search lifecycle:
const customMiddleware = ({ instantSearchInstance }) => {
  return {
    onStateChange({ uiState }) {
      console.log('State changed:', uiState);
    },
    subscribe() {
      console.log('Middleware subscribed');
    },
    started() {
      console.log('Search started');
    },
    unsubscribe() {
      console.log('Middleware unsubscribed');
    },
  };
};

search.use(customMiddleware);

Middleware Lifecycle Hooks

onStateChange
Function
Called when the UI state changes.
onStateChange({ uiState, setUiState }) {
  console.log('Current state:', uiState);
  // Optionally modify state
  setUiState(uiState);
}
subscribe
Function
Called when the middleware is added to InstantSearch.
subscribe() {
  console.log('Middleware initialized');
}
started
Function
Called when InstantSearch is started.
started() {
  console.log('Search is ready');
}
unsubscribe
Function
Called when the middleware is removed or InstantSearch is disposed.
unsubscribe() {
  console.log('Cleanup');
}

Middleware Examples

Analytics Tracking

const analyticsMiddleware = ({ instantSearchInstance }) => {
  return {
    onStateChange({ uiState }) {
      // Track search queries
      if (uiState.products?.query) {
        gtag('event', 'search', {
          search_term: uiState.products.query,
        });
      }
      
      // Track filter changes
      if (uiState.products?.refinementList) {
        gtag('event', 'filter', {
          filters: JSON.stringify(uiState.products.refinementList),
        });
      }
    },
  };
};

search.use(analyticsMiddleware);

State Persistence

const persistenceMiddleware = () => {
  return {
    onStateChange({ uiState }) {
      // Save to localStorage
      localStorage.setItem('searchState', JSON.stringify(uiState));
    },
    started() {
      // Restore from localStorage
      const savedState = localStorage.getItem('searchState');
      if (savedState) {
        search.setUiState(JSON.parse(savedState));
      }
    },
  };
};

search.use(persistenceMiddleware);

Debug Logger

const debugMiddleware = ({ instantSearchInstance }) => {
  return {
    subscribe() {
      instantSearchInstance.on('render', () => {
        console.log('UI rendered');
      });
      
      instantSearchInstance.mainHelper.on('result', ({ results }) => {
        console.log('Search results:', results);
      });
    },
    
    onStateChange({ uiState }) {
      console.log('State changed:', uiState);
    },
  };
};

search.use(debugMiddleware);

Using Middlewares

Add Middleware

const middleware = createInsightsMiddleware({ insightsClient: aa });
search.use(middleware);

Remove Middleware

search.unuse(middleware);

Multiple Middlewares

const insightsMiddleware = createInsightsMiddleware({ insightsClient: aa });
const analyticsMiddleware = createAnalyticsMiddleware();

search.use(insightsMiddleware, analyticsMiddleware);

Middleware Type

type Middleware = (options: {
  instantSearchInstance: InstantSearch;
}) => {
  onStateChange?(options: {
    uiState: UiState;
    setUiState: (uiState: UiState) => void;
  }): void;
  subscribe?(): void;
  started?(): void;
  unsubscribe?(): void;
};

Build docs developers (and LLMs) love