> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# dynamicWidgets

> Shows ordered facets and facet values based on index settings and rules.

```ts Signature theme={"system"}
dynamicWidgets({
  container: string | HTMLElement,
  widgets: function[],
  // Optional parameters
  transformItems?: function,
  fallbackWidget?: function,
  facets?: ['*']|[],
  maxValuesPerFacet?: number,
});
```

## Import

<CodeGroup>
  ```js Package manager theme={"system"}
  import { dynamicWidgets } from "instantsearch.js/es/widgets";
  ```

  ```js CDN theme={"system"}
  const { dynamicWidgets } = instantsearch.widgets;
  // or directly use instantsearch.widgets.dynamicWidgets()
  ```
</CodeGroup>

<Card title="See this widget in action" icon="monitor-play" href="https://instantsearchjs.netlify.app/stories/js/?path=/story/basics-dynamicWidgets--default" horizontal>
  Preview this widget and its behavior.
</Card>

## About this widget

`dynamicWidgets` is a widget that displays matching widgets,
based on the corresponding index settings and applied index rules.
You can configure the facet merchandising through the corresponding index setting.
To learn more, see [Facet display](/doc/guides/building-search-ui/ui-and-ux-patterns/facet-display/js).

### Requirements

You must set the attributes for faceting and configure the facet order,
either using the [dashboard](/doc/guides/managing-results/refine-results/faceting/how-to/declaring-attributes-for-faceting-with-dashboard) or with the API parameters [`attributesForFaceting`](/doc/api-reference/api-parameters/attributesForFaceting) and [`renderingContent`](/doc/api-reference/api-parameters/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.

You can override these settings with the [`facets`](#param-facets) and [`maxValuesPerFacet`](/doc/api-reference/widgets/dynamic-facets/js#param-max-values-per-facet) parameters.

<Note>
  You must use InstantSearch.js v4.72.0 or later to use `dynamicWidgets`.
</Note>

## Examples

<CodeGroup>
  ```js default theme={"system"}
  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 }),
  });
  ```

  ```js request customization theme={"system"}
  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 }),
    // do two network requests
    facets: [],
    // as many as you have pinned values, at least
    maxValuesPerFacet: 100,
  });
  ```
</CodeGroup>

## Options

<ParamField body="container" type="string | HTMLElement" required>
  The CSS Selector of the DOM element inside which the widget is inserted.

  <CodeGroup>
    ```js string theme={"system"}
    dynamicWidgets({
      // ...
      container: "#dynamic-widgets",
    });
    ```

    ```js HTMLElement theme={"system"}
    dynamicWidgets({
      // ...
      container: document.querySelector("#dynamic-widgets"),
    });
    ```
  </CodeGroup>
</ParamField>

<ParamField body="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.

  <CodeGroup>
    ```js widget theme={"system"}
    dynamicWidgets({
      // ...
      widgets: [(container) => refinementList({ container, attribute: "brand" })],
    });
    ```

    ```js hierarchical theme={"system"}
    dynamicWidgets({
      // ...
      widgets: [
        (container) =>
          hierarchicalMenu({
            container,
            attributes: [
              "hierarchicalCategories.lvl0",
              "hierarchicalCategories.lvl1",
            ],
          }),
      ],
    });
    ```

    ```js panel theme={"system"}
    dynamicWidgets({
      // ...
      widgets: [
        (container) =>
          panel({
            templates: { header: "Brand" },
          })(refinementList)({
            container,
            attribute: "brand",
          }),
      ],
    });
    ```

    ```js custom widget theme={"system"}
    const customRefinementList = connectRefinementList(() => {
      // implement custom widget
    });

    dynamicWidgets({
      // ...
      widgets: [
        (container) =>
          customRefinementList({
            container,
            attribute: "brand",
          }),
      ],
    });
    ```
  </CodeGroup>
</ParamField>

<ParamField body="fallbackWidget" type="(args: { attribute: string, container: HTMLElement }) => Widget">
  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.

  <CodeGroup>
    ```js widget theme={"system"}
    dynamicWidgets({
      // ...
      fallbackWidget: ({ container, attribute }) =>
        refinementList({ container, attribute }),
    });
    ```

    ```js panel theme={"system"}
    dynamicWidgets({
      // ...
      fallbackWidget: ({ container, attribute }) =>
        panel({
          templates: { header: attribute },
        })(refinementList)({ container, attribute }),
    });
    ```

    ```js custom widget theme={"system"}
    const customRefinementList = connectRefinementList(() => {
      // implement custom widget
    });

    dynamicWidgets({
      // ...
      fallbackWidget: ({ container, attribute }) =>
        customRefinementList({ container, attribute }),
    });
    ```
  </CodeGroup>
</ParamField>

<ParamField body="transformItems" type="function">
  A function to transform the attributes to render,
  or using a different source to determine the attributes to render.

  ```js JavaScript icon=code theme={"system"}
  dynamicWidgets({
    // ...
    transformItems(items, { results }) {
      return items;
    },
  });
  ```
</ParamField>

<ParamField body="facets" type="['*']|[]" default="['*']">
  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.

  <CodeGroup>
    ```js default theme={"system"}
    dynamicWidgets({
      // ...
      facets: ["*"],
    });
    ```

    ```js no facets theme={"system"}
    dynamicWidgets({
      // ...
      facets: [],
    });
    ```
  </CodeGroup>
</ParamField>

<ParamField body="maxValuesPerFacet" type="number" default={20}>
  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.

  ```js JavaScript icon=code theme={"system"}
  dynamicWidgets({
    // ...
    maxValuesPerFacet: 500,
  });
  ```
</ParamField>

## 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.

<CodeGroup>
  ```js Package manager theme={"system"}
  import { connectDynamicWidgets } from "instantsearch.js/es/connectors";
  ```

  ```js CDN theme={"system"}
  const { connectDynamicWidgets } = instantsearch.connectors;
  // or directly use instantsearch.connectors.connectDynamicWidgets()
  ```
</CodeGroup>

Then it's a 3-step process:

```js JavaScript icon=code theme={"system"}
// 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).

```js JavaScript icon=code theme={"system"}
const renderDynamicWidgets = (renderOptions, isFirstRender) => {
  const { attributesToRender, widgetParams } = renderOptions;

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

  // Render the widget
};
```

#### Rendering options

<ParamField body="attributesToRender" type="string[]">
  The list of refinement values to display returned from the Algolia API.

  ```js JavaScript icon=code theme={"system"}
  const renderDynamicWidgets = (renderOptions, isFirstRender) => {
    const { attributesToRender } = renderOptions;

    document.querySelector("#dynamic-widgets").innerHTML = `
      <ul>
        ${attributesToRender
          .map(
            (attribute) => `
              <li>
                ${attribute}
              </li>`,
          )
          .join("")}
      </ul>
    `;
  };
  ```
</ParamField>

<ParamField body="widgetParams" type="object">
  All original widget options forwarded to the render function.

  ```js JavaScript icon=code theme={"system"}
  const renderDynamicWidgets = (renderOptions, isFirstRender) => {
    const { widgetParams } = renderOptions;

    widgetParams.container.innerHTML = "...";
  };

  // ...

  search.addWidgets([
    customDynamicWidgets({
      // ...
      container: document.querySelector("#dynamic-widgets"),
    }),
  ]);
  ```
</ParamField>

### Create and instantiate the custom widget

First, create your custom widgets using a rendering function.
Then, instantiate them with parameters.

There are two kinds of parameters you can pass:

* **Instance parameters**. Predefined options that configure Algolia's behavior.
* **Custom parameters**. Parameters you define to make the widget reusable and adaptable.

Inside the `renderFunction`, both instance and custom parameters are accessible through `connector.widgetParams`.

```js JavaScript icon=code theme={"system"}
const customDynamicWidgets = connectDynamicWidgets(
  renderDynamicWidgets
);

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

#### Instance options

<ParamField body="widgets" type="object[]" required>
  The widgets to dynamically add to the parent index.
  You manually need to update their DOM position.

  ```js JavaScript icon=code theme={"system"}
  customDynamicWidgets({
    widgets: [
      refinementList({
        // ...
      }),
      customWidget({
        // ...
      }),
    ],
  });
  ```
</ParamField>

<ParamField body="transformItems" type="function">
  A function to transform the attributes to render,
  or using a different source to determine the attributes to render.

  ```js JavaScript icon=code theme={"system"}
  customDynamicWidgets({
    // ...
    transformItems(items, { results }) {
      return items;
    },
  });
  ```
</ParamField>

<ParamField body="facets" type="['*']|[]" default="['*']">
  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.

  <CodeGroup>
    ```js default theme={"system"}
    customDynamicWidgets({
      // ...
      facets: ["*"],
    });
    ```

    ```js no facets theme={"system"}
    customDynamicWidgets({
      // ...
      facets: [],
    });
    ```
  </CodeGroup>
</ParamField>

<ParamField body="maxValuesPerFacet" type="number" default={20}>
  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.

  ```js JavaScript icon=code theme={"system"}
  customDynamicWidgets({
    // ...
    maxValuesPerFacet: 500,
  });
  ```
</ParamField>

### Full example

<CodeGroup>
  ```html HTML theme={"system"}
  <div id="dynamic-widgets"></div>
  ```

  ```js JavaScript theme={"system"}
  // 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),
      ),
    }),
  ]);
  ```
</CodeGroup>
