UI libraries / InstantSearch.js / Widgets
Signature
dynamicWidgets({
  container: string|HTMLElement,
  widgets: function[],
  // Optional parameters
  transformItems: function,
  fallbackWidget: function,
  facets: ['*']|[],
  maxValuesPerFacet: number,
});
Import
1
import { dynamicWidgets } from 'instantsearch.js/es/widgets';

About this widget

DynamicWidgets is a widget that displays matching widgets, based on the corresponding settings of the index and may be altered by a Rule. You can configure the facet merchandising through the corresponding index setting.

Learn how to set up ordering in the facet display guide.

Requirements

You must set the attributes for faceting and configure the facet order, either using the dashboard or with the API parameters attributesForFaceting and renderingContent.

All matching widgets mount after the first network request completes. To avoid a second network request, facets are set to ['*'] and maxValuesPerFacet is set to 20 by default.

If this behavior isn’t what you want, it can be overridden using the facets and maxValuesPerFacet parameters.

You must use at least InstantSearch.js version 4.22.0 to use dynamicWidgets.

Examples

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
dynamicWidgets({
  container: '#dynamic-widgets',
  widgets: [
    container =>
      refinementList({ container, attribute: 'brand' }),
    container =>
      hierarchicalMenu({
        container,
        attributes: [
          'hierarchicalCategories.lvl0',
          'hierarchicalCategories.lvl1',
        ],
      }),
  ],
  fallbackWidget: ({ container, attribute }) =>
    panel({ templates: { header: attribute } })(
      menu
    )({ container, attribute }),
});

Options

container
type: string|HTMLElement
Required

The CSS Selector of the DOM element inside which the widget is inserted.

1
2
3
4
dynamicWidgets({
  // ...
  container: '#dynamic-widgets',
});
widgets
type: Array<(container: HTMLElement) => Widget>
Required

A list of creator functions for all refinement widgets you want to conditionally display. Each creator will receive a container and is expected to return a widget. The creator can also return custom widgets created with connectors.

Note that the returned widget needs to have an “attribute” or “attributes” argument, as this will be used to determine which widgets to render in which order.

1
2
3
4
5
6
7
dynamicWidgets({
  // ...
  widgets: [
    container =>
      refinementList({ container, attribute: 'brand' }),
  ],
});
fallbackWidget
type: (args: { attribute: string, container: HTMLElement }) => Widget
Optional

A creator function that is called for every attribute you want to conditionally display. The creator will receive a container and attribute and is expected to return a widget. The creator can also return custom widgets created with connectors.

1
2
3
4
5
dynamicWidgets({
  // ...
  fallbackWidget: ({ container, attribute }) =>
    refinementList({ container, attribute }),
});
transformItems
type: function
Optional

A function to transform the attributes to render, or using a different source to determine the attributes to render.

1
2
3
4
5
6
dynamicWidgets({
  // ...
  transformItems(items, { results }) {
    return items;
  },
});
facets
type: ['*']|[]
default: ['*']
Optional

The facets to apply before dynamic widgets get mounted. Setting the value to ['*'] will request all facets and avoid an additional network request once the widgets are added.

1
2
3
4
dynamicWidgets({
  // ...
  facets: ['*'],
});
maxValuesPerFacet
type: number
default: 20
Optional

The default number of facet values to request. It’s recommended to have this value at least as high as the highest limit and showMoreLimit of dynamic widgets, as this will prevent a second network request once that widget mounts.

To avoid pinned items not showing in the result, make sure you choose a maxValuesPerFacet at least as high as all the most pinned items you have.

1
2
3
4
dynamicWidgets({
  // ...
  maxValuesPerFacet: 500,
});

Customize the UI with connectDynamicWidgets

If you want to create your own UI of the dynamicWidgets widget, you can use connectors.

To use connectDynamicWidgets, you can import it with the declaration relevant to how you installed InstantSearch.js.

1
import { connectDynamicWidgets } from 'instantsearch.js/es/connectors';

Then it’s a 3-step process:

// 1. Create a render function
const renderDynamicWidgets = (renderOptions, isFirstRender) => {
  // Rendering logic
};

// 2. Create the custom widget
const customDynamicWidgets = connectDynamicWidgets(
  renderDynamicWidgets
);

// 3. Instantiate
search.addWidgets([
  customDynamicWidgets({
    // instance params
  })
]);

Create a render function

This rendering function is called before the first search (init lifecycle step) and each time results come back from Algolia (render lifecycle step).

const renderDynamicWidgets = (renderOptions, isFirstRender) => {
  const {
    string[] attributesToRender,
    object widgetParams,
  } = renderOptions;

  if (isFirstRender) {
    // Do some initial rendering and bind events
  }

  // Render the widget
}

Rendering options

attributesToRender
type: string[]

The list of refinement values to display returned from the Algolia API.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const renderDynamicWidgets = (renderOptions, isFirstRender) => {
  const { attributesToRender } = renderOptions;

  document.querySelector('#dynamic-widgets').innerHTML = `
    <ul>
      ${attributesToRender
        .map(
          attribute => `
            <li>
              ${attribute}
            </li>`
        )
        .join('')}
    </ul>
  `;
};
widgetParams
type: object

All original widget options forwarded to the render function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const renderDynamicWidgets = (renderOptions, isFirstRender) => {
  const { widgetParams } = renderOptions;

  widgetParams.container.innerHTML = '...';
};

// ...

search.addWidgets([
  customDynamicWidgets({
    // ...
    container: document.querySelector('#dynamic-widgets'),
  })
]);

Create and instantiate the custom widget

We first create custom widgets from our rendering function, then we instantiate them. When doing that, there are two types of parameters you can give:

  • Instance parameters: they are predefined parameters that you can use to configure the behavior of Algolia.
  • Your own parameters: to make the custom widget generic.

Both instance and custom parameters are available in connector.widgetParams, inside the renderFunction.

const customDynamicWidgets = connectDynamicWidgets(
  renderDynamicWidgets
);

search.addWidgets([
  customDynamicWidgets({
    widgets: object[],
    // Optional parameters
    transformItems: function,
    facets: string[],
    maxValuesPerFacet: number,
  })
]);

Instance options

widgets
type: object[]
Required

The widgets to dynamically be added to the parent index. Note that you manually will have change their DOM position.

1
2
3
4
5
6
customDynamicWidgets({
  widgets: [
    refinementList({ /* ... */ }),
    customWidget({ /* ... */ }),
  ],
});
transformItems
type: function
Optional

A function to transform the attributes to render, or using a different source to determine the attributes to render.

1
2
3
4
5
6
customDynamicWidgets({
  // ...
  transformItems(items, { results }) {
    return items;
  },
});
facets
type: ['*']|[]
default: ['*']
Optional

The facets to apply before dynamic widgets get mounted. Setting the value to ['*'] will request all facets and avoid an additional network request once the widgets are added.

1
2
3
4
customDynamicWidgets({
  // ...
  facets: ['*'],
});
maxValuesPerFacet
type: number
default: 20
Optional

The default number of facet values to request. It’s recommended to have this value at least as high as the highest limit and showMoreLimit of dynamic widgets, as this will prevent a second network request once that widget mounts.

To avoid pinned items not showing in the result, make sure you choose a maxValuesPerFacet at least as high as all the most pinned items you have.

1
2
3
4
customDynamicWidgets({
  // ...
  maxValuesPerFacet: 500,
});

Full example

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
// 0. Create a mapping between widgets and their attribute
const { rangeSlider, refinementList } = instantsearch.widgets;
const widgets = {
  brand: {
    widget: container => refinementList({ attribute: 'brand', container }),
    container: document.createElement('div'),
  },
  price: {
    widget: container => rangeSlider({ attribute: 'price', container }),
    container: document.createElement('div'),
  },
};

// 1. Create a render function
const renderDynamicWidgets = (renderOptions, isFirstRender) => {
  const {
    attributesToRender,
    widgetParams,
  } = renderOptions;

  if (isFirstRender) {
    const ul = document.createElement('ul');
    widgetParams.container.appendChild(ul);
  }

  const ul = widgetParams.container.querySelector('ul');
  ul.innerHTML = '';

  attributesToRender.forEach(attribute => {
    if (!widgets[attribute]) {
      return;
    }

    const container = widgets[attribute].container;
    ul.appendChild(container);
  });
};

// 2. Create the custom widget
const customDynamicWidgets = connectDynamicWidgets(
  renderDynamicWidgets
);

// 3. Instantiate
search.addWidgets([
  customDynamicWidgets({
    container: document.querySelector('#dynamic-widgets'),
    widgets: Object.values(widgets).map(({ widget, container }) =>
      widget(container)
    ),
  })
]);
Did you find this page helpful?