Guides / Solutions / Ecommerce / Filtering and Navigation

Visual Facets

Visual facets let you enrich your filtering UIs 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.

Requirements

Difficulty
Beginner
Prerequisites Instant Search 3+

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 front end.

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,
  • and 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 front-end mapping

You can implement visual facets by adding a mapping directly in your front-end application. 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. Note that this is a rudimentary implementation: it relies on your facets using 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 relies on your facets using 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?