🎉 Try the public beta of the new docs site at algolia.com/doc-beta! 🎉
Guides / Solutions / Ecommerce / Filtering and Navigation

Visual facets let you enrich your filtering UI with visual indications such as category images, colors, or logos. With visual facets, you can create better-looking layouts and search experiences. Visual facets are typically more engaging than plain text facets, thus increasing user engagement and encouraging discovery.

Before you begin

This tutorial requires InstantSearch.js.

Implementation guide

This guide provides two implementation options for visual facets: you can either add the necessary information to your records or do the mapping directly on your frontend.

Option 1: Adding visual facets to your records

One way of implementing visual facets is by embedding the information you’re trying to display directly in your facet names.

If you’re doing this on an existing Algolia index, you must update all your records.

Set the facet attributes of all your records with the following pattern:

  • The visual information (in this case, the URL of the image or the color code),
  • || as a separator,
  • The regular facet value.
1
2
3
4
5
{
  "name": "Black T-shirt",
  "color": "#000000||black",
  "availableIn": "https://source.unsplash.com/100x100/?paris||Paris"
}

If you’re creating a new index, use the saveObjects method with the just-mentioned facet values:

1
2
3
4
5
6
7
8
$res = $index->saveObjects([
    [
        'name' => 'Black T-shirt',
        'color' => '#000000||black',
        'availableIn' => 'https://source.unsplash.com/100x100/?paris||Paris',
        'objectID' => 'myID',
    ],
]);

If you’re updating an existing index, you can use a batch call of partialUpdateObjects.

You also need to set the facet attributes as attributesForFaceting:

1
2
3
4
5
6
$index->setSettings([
  'attributesForFaceting' => [
    "color",
    "availableIn"
  ]
]);

In the refinementList widget of your InstantSearch implementation, split your facet values on || to display the visual and attribute values.

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
// For color facets
search.addWidget(
  instantsearch.widgets.refinementList({
    container: '#facets',
    attribute: 'color',
    templates: {
      item: `
            <input type="checkbox" id="{{label.split("||")[1]}}" {{#isRefined}}checked{{/isRefined}}/>
            <label for="{{label.split("||")[1]}}" class="{{#isRefined}}isRefined{{/isRefined}}">
              {{label.split("||")[1]}}
              <span class="color" style="background-color: {{label.split("||")[0]}}"></span>
            </label>`
    }
  })
);

// For image facets
search.addWidget(
  instantsearch.widgets.refinementList({
    container: '#facets',
    attribute: 'availableIn',
    templates: {
      item: `
            <span class="location-pair" style="{{#isRefined}}font-weight: bold{{/isRefined}}">
              <img class="location-image" src="{{label.split(||)[0]}}""
              <span class="facet-value">{{label.split("||")[1]}} ({{count}})</span>
            <span>`
    }
  })
);

Option 2: Adding visual facets with a frontend mapping

You can implement visual facets by adding a mapping directly in your frontend app. This solution requires that you use a consistent naming scheme for your images and facet names.

First, create your mapping functions.

The following function takes the facet label of an image and returns the image with the .jpeg file extension tacked on. This is a rudimentary implementation: it requires your facets to use consistent image names.

1
const getLocationImage = location => location ? `${location.toLowerCase()}.jpeg` : "";

The following function takes a color facet and returns either the color or “transparent” if nothing was passed. Again, this is a rudimentary implementation: it doesn’t return the hexadecimal color code but instead requires your facets to use valid CSS color keywords.

1
const getHexaCodeFromColor = color => color ? color : 'transparent';

In your refinementList widget, use your mapping functions to transform each facet.

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
// For color facets
search.addWidget(
  instantsearch.widgets.refinementList({
    container: '#facets',
    attribute: 'color',
    transformItems(items) {
      return items.map(item => ({
        ...item,
        hexaCode: getHexaCodeFromColor(item.label)
      }));
    },
    templates: {
      item: `
        <input type="checkbox" id="{{label}}" {{#isRefined}}checked{{/isRefined}}/>
        <label for="{{label}}" class="{{#isRefined}}isRefined{{/isRefined}}">
          {{label}}
          <span class="color" style="background-color: {{hexaCode}}"></span>
        </label>`
    }
  })
);

// For image facets
search.addWidget(
  instantsearch.widgets.refinementList({
    container: '#facets',
    attribute: 'availableIn',
    transformItems(items) {
      return items.map(item => ({
        ...item,
        image: getLocationImage(item.label)
      }));
    },
    templates: {
      item: `<span class="location-pair" style="{{#isRefined}}font-weight: bold{{/isRefined}}">
              <img class="location-image" src="assets/images/locations/{{image}}">
              <span class="facet-value">{{label}} ({{count}})</span>
            <span>`
    }
  })
);
Did you find this page helpful?