Upgrading from v0 to v1

This is for:


Headless v1 introduces changes that improve the library’s performance, make it easier to use, and let you scale projects by supporting additional use cases.

The following are breaking changes from Headless v0 to v1

The Headless engine

The HeadlessEngine class has been replaced with smaller builder functions, each of which is dedicated to a specific use case. Each function exposes only the options that are relevant to its intended use case.

Headless Version 0

import { HeadlessEngine} from @coveo/headless’;

const engine = new HeadlessEngine(...)

Headless Version 1

import { buildSearchEngine } from '@coveo/headless';
import { buildRecommendationEngine } from '@coveo/headless/recommendation';
import { buildProductRecommendationEngine } from '@coveo/headless/product-recommendation'
const engine = buildSearchEngine(...);
const recommendationEngine = buildRecommendationEngine(...);
const productRecommendationEngine = buildProductRecommendationEngine(...);

You also no longer need to configure reducers. The library now manages reducers behind the scenes, based on the kind of engine you’re using , and the controllers you’re using the engine with. Aside from being easier to use, implementations using a subset of controllers should benefit from a smaller bundle size and a faster initial load.

Note also that each use case is now imported from a dedicated sub-package. A sub-package groups together the exports that are relevant for a use case. If an export is missing from a sub-package, it’s likely because it’s incompatible with the use case.

For example, the buildInteractiveResult controller is no longer used for recommendation results because it logs an analytics event that’s specific to the search use case.

As a rule, there should be no need to mix exports from two different sub-packages. If you believe an export is missing from a sub-package, please reach out to us so that we can investigate.

The renewAccessToken concept is no longer a method on the engine instance; rather it’s now exposed as an engine configuration option. When the function is specified, a Headless engine will automatically call it to obtain a new token if it detects a 419 HTTP status code.

Preproccesing requests

In Headless version 0, preprocessing search requests before they were sent to Coveo was done by defining a preprocessRequestMiddleware function under the search property of the Headless engine’s configuration object.

Headless Version 0

const engine = new HeadlessEngine({
  search: {
    preprocessRequestMiddleware: (request)=> {
      // modify request object and return it;
      return request;

As of Headless version 1, this is achieved by defining a preprocessRequest function as a first-class property of the engine’s configuration object. This function can now also be used to pre-process any type of request to Coveo, not only search-related ones.


The preprocessRequest method is a powerful tool, and it can be leveraged to do things that should be done in a different manner. For example, you can use it to set aq, but you should use the Headless AdvancedSearchQuery action instead.

If you have to use preprocessRequest, you should code defensively. For example, you can implement try…​catch to prevent errors.

Headless Version 1

const engine = buildSearchEngine({
  preprocessRequest: (request, origin)=> {
    // modify request object and return it;
    // optionally use the origin of the request before deciding to do any modification.
    return request;

Action loader functions

Action namespaces have been replaced with loader functions. The change goes hand-in-hand with the goal of removing the need to configure reducers upfront. This prevents situations where a dispatched action has no effect, while also making it possible to generate smaller and faster bundles.

Headless Version 0

import { SearchActions } from '@coveo/headless';
import { Engine } from '.App/Engine.ts';
const action = SearchActions.executeSearch(/* ... */);

Headless Version 1

import { loadSearchActions } from '@coveo/headless';
import { engine } from '.App/Engine.ts';
const actions = loadSearchActions(engine);
const action = actions.executeSearch(/* ... */);

The Tab controller

The id option on the Tab controller is now required. The analytics reports will use the id value, so we recommended that you choose a human-readable value that captures what gets displayed when the tab is selected.

const messageExpression = buildQueryExpression()
    field: 'objecttype',
    operator: 'isExactly',
    values: ['Message'],

const messagesTab = buildTab(engine, {
  options: {
    id: 'messages',
    expression: messageExpression,

The SearchBox controller

The hideSuggestions method on the SearchBox controller has been removed. Use CSS to hide query suggestions on blur instead of calling hideSuggestions, which would clear them from the Headless store. This approach avoids needing to make a second request on focus, which would create an unnecessary delay before users can see the suggestions.