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

# Visual facets

> Enhance users' experiences with using filters by adding visual indicators such as a color palette or images.

export const Records = () => <Tooltip tip="A record is a searchable object in an Algolia index. Each record consists of named attributes." cta="Algolia records" href="/doc/guides/sending-and-managing-data/prepare-your-data#algolia-records">
    records
  </Tooltip>;

export const Index = () => <Tooltip tip="An Algolia index is a searchable dataset that consists of records and configuration settings. These settings define how the records are searched and ranked.">
    index
  </Tooltip>;

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.

<Columns>
  <Card title="Open CodeSandbox" icon="codesandbox" href="https://codesandbox.io/s/github/algolia/solutions/tree/master/visual-facets-demo">
    Run and edit the Visual facets example in CodeSandbox.
  </Card>

  <Card title="View code on GitHub" icon="github" href="https://github.com/algolia/solutions/tree/master/visual-facets-demo">
    View the Visual facets example code on GitHub.
  </Card>
</Columns>

## Before you begin

This tutorial requires [InstantSearch.js](/doc/guides/building-search-ui/getting-started/js).

## Implementation guide

This guide provides two implementation options for visual facets:
you can either add the visual facet to your records or map the visual facets to your frontend.

<AccordionGroup>
  <Accordion title="Add 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.

    <Info>
      If you're doing this on an existing Algolia <Index />,
      you must update all your <Records />.
    </Info>

    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.

    ```json JSON icon=braces theme={"system"}
    {
      "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`](/doc/libraries/sdk/v1/methods/save-objects) method with the just-mentioned facet values:

    <CodeGroup>
      ```cs C# theme={"system"}
      var response = await client.SaveObjectAsync(
        "INDEX_NAME",
        new Dictionary<string, string>
        {
          { "name", "Black T-shirt" },
          { "color", "#000000||black" },
          { "availableIn", "https://source.unsplash.com/100x100/?paris||Paris" },
          { "objectID", "myID" },
        }
      );
      ```

      ```dart Dart theme={"system"}
      final response = await client.saveObject(
        indexName: "INDEX_NAME",
        body: {
          'name': "Black T-shirt",
          'color': "#000000||black",
          'availableIn': "https://source.unsplash.com/100x100/?paris||Paris",
          'objectID': "myID",
        },
      );
      ```

      ```go Go theme={"system"}
      response, err := client.SaveObject(client.NewApiSaveObjectRequest(
        "INDEX_NAME",
        map[string]any{
          "name":        "Black T-shirt",
          "color":       "#000000||black",
          "availableIn": "https://source.unsplash.com/100x100/?paris||Paris",
          "objectID":    "myID",
        },
      ))
      if err != nil {
        // handle the eventual error
        panic(err)
      }
      ```

      ```java Java theme={"system"}
      SaveObjectResponse response = client.saveObject(
        "INDEX_NAME",
        new HashMap() {
          {
            put("name", "Black T-shirt");
            put("color", "#000000||black");
            put("availableIn", "https://source.unsplash.com/100x100/?paris||Paris");
            put("objectID", "myID");
          }
        }
      );
      ```

      ```js JavaScript theme={"system"}
      const response = await client.saveObject({
        indexName: 'INDEX_NAME',
        body: {
          name: 'Black T-shirt',
          color: '#000000||black',
          availableIn: 'https://source.unsplash.com/100x100/?paris||Paris',
          objectID: 'myID',
        },
      });
      ```

      ```kotlin Kotlin theme={"system"}
      var response =
        client.saveObject(
          indexName = "INDEX_NAME",
          body =
            buildJsonObject {
              put("name", JsonPrimitive("Black T-shirt"))
              put("color", JsonPrimitive("#000000||black"))
              put("availableIn", JsonPrimitive("https://source.unsplash.com/100x100/?paris||Paris"))
              put("objectID", JsonPrimitive("myID"))
            },
        )
      ```

      ```php PHP theme={"system"}
      $response = $client->saveObject(
          'INDEX_NAME',
          ['name' => 'Black T-shirt',
              'color' => '#000000||black',
              'availableIn' => 'https://source.unsplash.com/100x100/?paris||Paris',
              'objectID' => 'myID',
          ],
      );
      ```

      ```python Python theme={"system"}
      response = client.save_object(
          index_name="INDEX_NAME",
          body={
              "name": "Black T-shirt",
              "color": "#000000||black",
              "availableIn": "https://source.unsplash.com/100x100/?paris||Paris",
              "objectID": "myID",
          },
      )
      ```

      ```ruby Ruby theme={"system"}
      response = client.save_object(
        "INDEX_NAME",
        {
          name: "Black T-shirt",
          color: "#000000||black",
          availableIn: "https://source.unsplash.com/100x100/?paris||Paris",
          objectID: "myID"
        }
      )
      ```

      ```scala Scala theme={"system"}
      val response = Await.result(
        client.saveObject(
          indexName = "INDEX_NAME",
          body = JObject(
            List(
              JField("name", JString("Black T-shirt")),
              JField("color", JString("#000000||black")),
              JField("availableIn", JString("https://source.unsplash.com/100x100/?paris||Paris")),
              JField("objectID", JString("myID"))
            )
          )
        ),
        Duration(100, "sec")
      )
      ```

      ```swift Swift theme={"system"}
      let response = try await client.saveObject(
          indexName: "INDEX_NAME",
          body: [
              "name": "Black T-shirt",
              "color": "#000000||black",
              "availableIn": "https://source.unsplash.com/100x100/?paris||Paris",
              "objectID": "myID",
          ]
      )
      ```
    </CodeGroup>

    If you're updating an existing index, you can use the [`partialUpdateObjects`](/doc/libraries/sdk/v1/methods/partial-update-objects) method.

    You also need to set the facet attributes as [`attributesForFaceting`](/doc/api-reference/api-parameters/attributesForFaceting):

    <CodeGroup>
      ```cs C# theme={"system"}
      var response = await client.SetSettingsAsync(
        "INDEX_NAME",
        new IndexSettings
        {
          AttributesForFaceting = new List<string> { "color", "availableIn" },
        }
      );
      ```

      ```dart Dart theme={"system"}
      final response = await client.setSettings(
        indexName: "INDEX_NAME",
        indexSettings: IndexSettings(
          attributesForFaceting: [
            "color",
            "availableIn",
          ],
        ),
      );
      ```

      ```go Go theme={"system"}
      response, err := client.SetSettings(client.NewApiSetSettingsRequest(
        "INDEX_NAME",
        search.NewEmptyIndexSettings().SetAttributesForFaceting(
          []string{"color", "availableIn"})))
      if err != nil {
        // handle the eventual error
        panic(err)
      }
      ```

      ```java Java theme={"system"}
      UpdatedAtResponse response = client.setSettings(
        "INDEX_NAME",
        new IndexSettings().setAttributesForFaceting(Arrays.asList("color", "availableIn"))
      );
      ```

      ```js JavaScript theme={"system"}
      const response = await client.setSettings({
        indexName: 'INDEX_NAME',
        indexSettings: { attributesForFaceting: ['color', 'availableIn'] },
      });
      ```

      ```kotlin Kotlin theme={"system"}
      var response =
        client.setSettings(
          indexName = "INDEX_NAME",
          indexSettings = IndexSettings(attributesForFaceting = listOf("color", "availableIn")),
        )
      ```

      ```php PHP theme={"system"}
      $response = $client->setSettings(
          'INDEX_NAME',
          ['attributesForFaceting' => [
              'color',

              'availableIn',
          ],
          ],
      );
      ```

      ```python Python theme={"system"}
      response = client.set_settings(
          index_name="INDEX_NAME",
          index_settings={
              "attributesForFaceting": [
                  "color",
                  "availableIn",
              ],
          },
      )
      ```

      ```ruby Ruby theme={"system"}
      response = client.set_settings(
        "INDEX_NAME",
        Algolia::Search::IndexSettings.new(attributes_for_faceting: ["color", "availableIn"])
      )
      ```

      ```scala Scala theme={"system"}
      val response = Await.result(
        client.setSettings(
          indexName = "INDEX_NAME",
          indexSettings = IndexSettings(
            attributesForFaceting = Some(Seq("color", "availableIn"))
          )
        ),
        Duration(100, "sec")
      )
      ```

      ```swift Swift theme={"system"}
      let response = try await client.setSettings(
          indexName: "INDEX_NAME",
          indexSettings: IndexSettings(attributesForFaceting: ["color", "availableIn"])
      )
      ```
    </CodeGroup>

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

    ```js JavaScript icon=code theme={"system"}
    // For color facets
    search.addWidget(
      instantsearch.widgets.refinementList({
        container: "#facets",
        attribute: "color",
        templates: {
          item({ label, isRefined }, { html }) {
            const [color, name] = label.split("||");
            return html`<div>
              <input type="checkbox" id="${name}" checked="${isRefined}" />
              <label>
                ${name}
                <span class="color" style="${{ "background-color": color }}"></span>
              </label>
            </div>`;
          },
        },
      }),
    );

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

  <Accordion title="Map the visual facets to your frontend">
    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.

    ```js JavaScript icon=code theme={"system"}
    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](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords).

    ```js JavaScript icon=code theme={"system"}
    const getHexCodeFromColor = color => color ? color : 'transparent';
    ```

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

    ```js JavaScript icon=code theme={"system"}
    // For color facets
    search.addWidget(
      instantsearch.widgets.refinementList({
        container: '#facets',
        attribute: 'color',
        transformItems(items) {
          return items.map(item => ({
            ...item,
            hexCode: getHexCodeFromColor(item.label)
          }));
        },
        templates: {
          item({ label, hexCode, isRefined }, { html }) {
            return html`
              <input type="checkbox" id="${label}" checked="${isRefined ? "checked" : ""}" />
              <label for="${label}" class="${isRefined ? "isRefined" : ""}">
                ${label}
                <span class="color" style="background-color: ${hexCode}"></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({ label, image, isRefined }, { html }) {
            return html`
              <span class="location-pair" style="${isRefined ? "font-weight: bold" : ""}">
                <img class="location-image" src="assets/images/locations/${image}" />
                <span class="facet-value">${label} (${count})</span>
              </span>
            `;
          },
        },
      })
    );
    ```
  </Accordion>
</AccordionGroup>

## See also

* [Declare attributes for faceting in the dashboard](/doc/guides/managing-results/refine-results/faceting/how-to/declaring-attributes-for-faceting-with-dashboard)
* [Declare attributes for faceting with the API](/doc/guides/managing-results/refine-results/faceting/how-to/declaring-attributes-for-faceting)
* [Auto-selected facets](/doc/guides/solutions/ecommerce/filtering-and-navigation/tutorials/auto-selected-facets)
* [Guided search](/doc/guides/solutions/ecommerce/filtering-and-navigation/tutorials/guided-search)
* [Visual facets](/doc/guides/solutions/ecommerce/filtering-and-navigation/tutorials/visual-facets)
