API Reference / InstantSearch.js Widgets / currentRefinements
Widget signature
instantsearch.widgets.currentRefinements({
  container: string|HTMLElement,
  // Optional parameters
  includedAttributes: string[],
  excludedAttributes: string[],
  cssClasses: object,
  transformItems: function,
});

About this widget # A

The currentRefinements widget displays a list of refinements applied to the search.

Examples # A

1
2
3
instantsearch.widgets.currentRefinements({
  container: '#current-refinements',
});

Options # A

container #
type: string|HTMLElement
Required

The CSS Selector or HTMLElement to insert the widget into.

1
2
3
instantsearch.widgets.currentRefinements({
  container: '#current-refinements',
});
includedAttributes #
type: string[]
default: []
Optional

The attributes to include in the widget (all by default). Cannot be used with excludedAttributes. In the example below, only the categories attribute is included in the widget.

1
2
3
4
instantsearch.widgets.currentRefinements({
  // ...
  includedAttributes: ['categories'],
});
excludedAttributes #
type: string[]
default: ["query"]
Optional

The attributes to exclude from the widget. Cannot be used with includedAttributes. In the example below, the brand attribute is excluded from the widget.

1
2
3
4
instantsearch.widgets.currentRefinements({
  // ...
  excludedAttributes: ['brand'],
});
cssClasses #
type: object
default: {}
Optional

The CSS classes to override.

  • root: the root element of the widget.
  • list: the list element.
  • item: the list items.
  • label: the label element of each item.
  • category: the container of each item value.
  • categoryLabel: the text element of each item value.
  • delete: the button element of eatch item value.
1
2
3
4
5
6
7
8
9
10
instantsearch.widgets.currentRefinements({
  // ...
  cssClasses: {
    root: 'MyCustomCurrentRefinements',
    list: [
      'MyCustomCurrentRefinementsList',
      'MyCustomCurrentRefinementsList--subclass',
    ],
  },
});
transformItems #
type: function
default: items => items
Optional

Receives the items, and is called before displaying them. Should return a new array with the same shape as the original array. Useful for mapping over the items to transform, and remove or reorder them.

1
2
3
4
5
6
instantsearch.widgets.currentRefinements({
  // ...
  transformItems(items) {
    return items.filter(item => item.attribute !== 'brand');
  },
});

HTML output# A

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
<div class="ais-CurrentRefinements">
  <ul class="ais-CurrentRefinements-list">
    <li class="ais-CurrentRefinements-item">
      <span class="ais-CurrentRefinements-label">
        Category:
      </span>
      <span class="ais-CurrentRefinements-category">
        <span class="ais-CurrentRefinements-categoryLabel">
          Movies & TV Shows
        </span>
        <button class="ais-CurrentRefinements-delete"></button>
      </span>
      <span class="ais-CurrentRefinements-category">
        <span class="ais-CurrentRefinements-categoryLabel">
          Others
        </span>
        <button class="ais-CurrentRefinements-delete"></button>
      </span>
    </li>
    <li class="ais-CurrentRefinements-item">
      <span class="ais-CurrentRefinements-label">
        Brand:
      </span>
      <span class="ais-CurrentRefinements-category">
        <span class="ais-CurrentRefinements-categoryLabel">
          Algolia
        </span>
        <button class="ais-CurrentRefinements-delete"></button>
      </span>
    </li>
  </ul>
</div>

Customize the UI - connectCurrentRefinements# A

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

It’s a 3-step process:

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

// 2. Create the custom widget
const customCurrentRefinements = instantsearch.connectors.connectCurrentRefinements(
  renderCurrentRefinements
);

// 3. Instantiate
search.addWidgets([
  customCurrentRefinements({
    // 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 renderCurrentRefinements = (renderOptions, isFirstRender) => {
  const {
    object[] items,
    boolean canRefine,
    function refine,
    function createURL,
    object widgetParams,
  } = renderOptions;

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

  // Render the widget
}

Rendering options #

items #
type: object[]

All the currently refined items, grouped by attribute.

With each item:

  • attribute: string: the attribute on which the refinement is applied.
  • label: string: The textual representation of this attribute.
  • refine: function: removes the refinement.
  • refinements: object[]: currently applied refinements.

With each refinement:

  • type: 'facet' | 'exclude' | 'disjunctive' | 'hierarchical' | 'numeric' | 'query': the type of the refinement.
  • attribute: string: the attribute on which the refinement is applied.
  • label: string: the label of the refinement to display.
  • value: string: the raw value of the refinement.
  • operator?: string: the value of the operator (only if applicable).
  • exhaustive?: boolean: whether the count is exhaustive (only if applicable).
  • count?: number: the number of found items (only if applicable).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const renderListItem = item => `
  <li>
    ${item.attribute}
    <ul>
      ${item.refinements
        .map(refinement => `<li>${refinement.value} (${refinement.count})</li>`)
        .join('')}
    </ul>
  </li>
`;

const renderCurrentRefinements = (renderOptions, isFirstRender) => {
  const { items } = renderOptions;

  document.querySelector('#current-refinements').innerHTML = `
    <ul>
      ${items.map(renderListItem).join('')}
    </ul>
  `;
};
canRefine #
type: boolean
Required

Indicates if search state can be refined.

1
2
3
4
5
6
7
8
const renderCurrentRefinements = (renderOptions, isFirstRender) => {
  const { canRefine } = renderOptions;

  if (!canRefine) {
    document.querySelector('#current-refinements').innerHTML = '';
    return;
  }
};
refine #
type: function

Clears a single refinement and triggers a new search.

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
const createDataAttribtues = refinement =>
  Object.keys(refinement)
    .map(key => `data-${key}="${refinement[key]}"`)
    .join(' ');

const renderListItem = item => `
  <li>
    ${item.label}
    <ul>
      ${item.refinements
        .map(
          refinement =>
            `<li>
              ${refinement.label} (${refinement.count})
              <button ${createDataAttribtues(refinement)}>X</button>
            </li>`
        )
        .join('')}
    </ul>
  </li>
`;

const renderCurrentRefinements = (renderOptions, isFirstRender) => {
  const { items, refine } = renderOptions;

  const container = document.querySelector('#current-refinements');

  container.innerHTML = `
    <ul>
      ${items.map(renderListItem).join('')}
    </ul>
  `;

  [...container.querySelectorAll('button')].forEach(element => {
    element.addEventListener('click', event => {
      const item = Object.keys(event.currentTarget.dataset).reduce(
        (acc, key) => ({
          ...acc,
          [key]: event.currentTarget.dataset[key],
        }),
        {}
      );

      refine(item);
    });
  });
};
createURL #
type: function

Generates a URL for the next state.

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
const renderListItem = ({ item, createURL }) => `
  <li>
    ${item.label}
    <ul>
      ${item.refinements
        .map(
          refinement =>
            `<li>
              <a href="${createURL(refinement)}">
                ${refinement.label} (${refinement.count})
              </a>
            </li>`
        )
        .join('')}
    </ul>
  </li>
`;

const renderCurrentRefinements = (renderOptions, isFirstRender) => {
  const { items, createURL } = renderOptions;

  document.querySelector('#current-refinements').innerHTML = `
    <ul>
      ${items.map(item => renderListItem({ item, createURL })).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
const renderCurrentRefinements = (renderOptions, isFirstRender) => {
  const { widgetParams } = renderOptions;

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

// ...

search.addWidgets([
  customCurrentRefinements({
    container: document.querySelector('#current-refinements'),
  })
]);

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 customCurrentRefinements = instantsearch.connectors.connectCurrentRefinements(
  renderCurrentRefinements
);

search.addWidgets([
  customCurrentRefinements({
    // Optional parameters
    includedAttributes: string[],
    excludedAttributes: string[],
    transformItems: function,
  })
]);

Instance options #

includedAttributes #
type: string[]
default: []
Optional

The attributes to include in the widget (all by default). Cannot be used with excludedAttributes. In the example below, only the categories attribute is included in the widget.

1
2
3
customCurrentRefinements({
  includedAttributes: ['categories'],
});
excludedAttributes #
type: string[]
default: ["query"]
Optional

The attributes to exclude from the widget. Cannot be used with includedAttributes. In the example below, the brand attribute is excluded from the widget.

1
2
3
customCurrentRefinements({
  excludedAttributes: ['brand'],
});
transformItems #
type: function
default: items => items
Optional

Receives the items, and is called before displaying them. Should return a new array with the same shape as the original array. Useful for mapping over the items to transform, and remove or reorder them.

1
2
3
4
5
customCurrentRefinements({
  transformItems(items) {
    return items.filter(item => item.attribute !== 'brand');
  },
});

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
53
54
55
56
57
58
// Create the render function
const createDataAttribtues = refinement =>
  Object.keys(refinement)
    .map(key => `data-${key}="${refinement[key]}"`)
    .join(' ');

const renderListItem = item => `
  <li>
    ${item.label}
    <ul>
      ${item.refinements
        .map(
          refinement =>
            `<li>
              ${refinement.label} (${refinement.count})
              <button ${createDataAttribtues(refinement)}>X</button>
            </li>`
        )
        .join('')}
    </ul>
  </li>
`;

const renderCurrentRefinements = (renderOptions, isFirstRender) => {
  const { items, refine, widgetParams } = renderOptions;

  widgetParams.container.innerHTML = `
    <ul>
      ${items.map(renderListItem).join('')}
    </ul>
  `;

  [...widgetParams.container.querySelectorAll('button')].forEach(element => {
    element.addEventListener('click', event => {
      const item = Object.keys(event.currentTarget.dataset).reduce(
        (acc, key) => ({
          ...acc,
          [key]: event.currentTarget.dataset[key],
        }),
        {}
      );

      refine(item);
    });
  });
};

// Create the custom widget
const customCurrentRefinements = instantsearch.connectors.connectCurrentRefinements(
  renderCurrentRefinements
);

// Instantiate the custom widget
search.addWidgets([
  customCurrentRefinements({
    container: document.querySelector('#current-refinements'),
  })
]);

Did you find this page helpful?