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

# Add banners

> Display promotional or informational banners in your Algolia results using Rules.

export const Application = () => <Tooltip tip="An Algolia application is a self-contained environment with its own indices, configuration, and API keys. Applications don't share data or settings with each other.">
    application
  </Tooltip>;

export const AlgoliaSearch = () => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="20" height="20" className="inline" fill="none" role="presentation" ariaLabel="Algolia Search">
    <circle cx="40" cy="32" r="28" fill="#5468FF"></circle>
    <rect x="30" y="22" width="20" height="20" rx="10" fill="#fff"></rect>
    <path d="M43 63.5 54.5 60l6 17h-12L43 63.5Z" fill="#36395A"></path>
  </svg>;

Search isn't only about retrieving results.
Depending on the query, you might want to show banners, such as informational or promotional content.

For example, consider an online bookstore that wants to display a discount for users looking for Harry Potter titles.
You can display a banner for specific queries using a rule.

You can add banners in two ways:

* **[InstantSearch banners](#instantsearch-banners)**: use a rule to add banner data to the `renderingContent.widgets.banners` field, so that supported InstantSearch widgets render the banner for you.
* **[Custom banners](#custom-banners)**: use a rule to add banner data to the `userData` field. Your own UI code reads this data and renders the banner.

## InstantSearch banners

If your site already uses an InstantSearch [`Hits`](/doc/api-reference/widgets/hits/js) or [`InfiniteHits`](/doc/api-reference/widgets/infinite-hits/js)
widget, you can display banners automatically based on a Visual Editor rule which adds banner data to the search response.

<Note>
  This requires InstantSearch.js version 4.73.0 or later, React InstantSearch version 7.12.0 or later, or Vue InstantSearch version 4.19.0 or later.
</Note>

### Create a Visual Editor rule with a banner consequence

1. Click **Create your first Rule** or **New rule** and select **Visual Editor**.
2. In the **It all starts here** section, click **Set query conditions**.
3. In the **Your query** field, enter `harry potter` and click **Apply**.
4. In the **What do you want to do?** section, click **Add Banner**.
5. Enter the required content for your banner: the URL for your banner image and click **Apply**.
6. Review the banner preview above the results.
7. Confirm your changes by clicking **Review and Publish**.

<Check>
  Make sure your search UI uses a supported InstantSearch widget (`Hits` or `InfiniteHits`) and
  is deployed on your website so it can [display banners](#instantsearch-banners) from the `renderingContent.widgets.banners` property in the API response.
</Check>

### Create a rule with a banner consequence using the API

To run the code examples on this page, [install the latest API client](/doc/libraries/sdk/install).

To add a rule with a banner consequence using the API, use the [`saveRule`](/doc/libraries/sdk/v1/methods/save-rule) method.
When adding a rule, you need to define a condition and a consequence.
In the consequence, [add search parameters](/doc/guides/managing-results/rules/rules-overview/how-to/add-default-search-parameters-with-rules) and configure [`renderingContent`](/doc/api-reference/api-parameters/renderingContent) using the following template:

```json JSON icon=braces theme={"system"}
{
  "renderingContent": {
    "widgets": {
      "banners": [
        {
          "image": {
            "urls": [
              {
                "url": "https://www.algolia.com/banner.jpg"
              }
            ],
            "title": "20% OFF on all Harry Potter books!"
          },
          "link": {
            "url": "https://www.algolia.com/harry-potter-promo",
            "target": "_blank"
          }
        }
      ]
    }
  }
}
```

### Render banner with InstantSearch

After adding the rule, you can display the banner in your UI with the InstantSearch `Hits` or `InfiniteHits` widgets.

The banner renders automatically unless you configure the widget to hide it.
For example:

<CodeGroup>
  ```js JavaScript theme={"system"}
  // Don't display the banner in the Hits widget
  search.addWidgets([
    instantsearch.widgets.hits({
      container: "#hits",
      templates: {
        banner: (_results) => null,
      },
    }),
  ]);
  ```

  ```jsx React theme={"system"}
  // Don't display the banner in the Hits widget
  <Hits bannerComponent={false} />;
  ```

  ```vue Vue theme={"system"}
  <!-- Don't display the banner in the Hits widget -->
  <ais-hits :showBanner="false" />
  ```
</CodeGroup>

## Custom banners

If you don't use InstantSearch, or you need a fully custom banner, use this approach.

### Create a rule for returning custom data

If you want to motivate users to buy Harry Potter books,
you can display a special promotional banner at the top of search results whenever their search query contains `harry potter`.
To display the banner:

* Create a rule that returns the banner data in the `userData` field.
* Update your search UI to read and render this `userData`.

You can add the rule [using the API](#create-a-rule-with-the-api) or the [Algolia dashboard](#create-a-rule-in-the-dashboard),
then [retrieve the banner data in your UI](#retrieve-the-banner-data-in-your-ui).

#### Create a rule with the API

To add a rule, you need to use the [`saveRule`](/doc/libraries/sdk/v1/methods/save-rule) method.
When setting a rule, you need to define a condition and a consequence.

For the preceding example, you want to show the banner whenever the query contains `harry potter`.
The match doesn't need to be exact.
If the query is `harry potter azkaban` or `books harry potter`, you still want to display the promotion.

<CodeGroup>
  ```cs C# theme={"system"}
  var response = await client.SaveRuleAsync(
    "INDEX_NAME",
    "harry-potter-rule",
    new Rule
    {
      ObjectID = "harry-potter-rule",
      Conditions = new List<Condition>
      {
        new Condition { Pattern = "harry potter", Anchoring = Enum.Parse<Anchoring>("Contains") },
      },
      Consequence = new Consequence
      {
        UserData = new Dictionary<string, string>
        {
          { "promo_content", "20% OFF on all Harry Potter books!" },
        },
      },
    }
  );
  ```

  ```dart Dart theme={"system"}
  final response = await client.saveRule(
    indexName: "INDEX_NAME",
    objectID: "harry-potter-rule",
    rule: Rule(
      objectID: "harry-potter-rule",
      conditions: [
        Condition(
          pattern: "harry potter",
          anchoring: Anchoring.fromJson("contains"),
        ),
      ],
      consequence: Consequence(
        userData: {
          'promo_content': "20% OFF on all Harry Potter books!",
        },
      ),
    ),
  );
  ```

  ```go Go theme={"system"}
  response, err := client.SaveRule(client.NewApiSaveRuleRequest(
    "INDEX_NAME", "harry-potter-rule",
    search.NewEmptyRule().SetObjectID("harry-potter-rule").SetConditions(
      []search.Condition{*search.NewEmptyCondition().SetPattern("harry potter").SetAnchoring(search.Anchoring("contains"))}).SetConsequence(
      search.NewEmptyConsequence().SetUserData(map[string]any{"promo_content": "20% OFF on all Harry Potter books!"}))))
  if err != nil {
    // handle the eventual error
    panic(err)
  }
  ```

  ```java Java theme={"system"}
  UpdatedAtResponse response = client.saveRule(
    "INDEX_NAME",
    "harry-potter-rule",
    new Rule()
      .setObjectID("harry-potter-rule")
      .setConditions(Arrays.asList(new Condition().setPattern("harry potter").setAnchoring(Anchoring.CONTAINS)))
      .setConsequence(
        new Consequence().setUserData(
          new HashMap() {
            {
              put("promo_content", "20% OFF on all Harry Potter books!");
            }
          }
        )
      )
  );
  ```

  ```js JavaScript theme={"system"}
  const response = await client.saveRule({
    indexName: 'indexName',
    objectID: 'harry-potter-rule',
    rule: {
      objectID: 'harry-potter-rule',
      conditions: [{ pattern: 'harry potter', anchoring: 'contains' }],
      consequence: { userData: { promo_content: '20% OFF on all Harry Potter books!' } },
    },
  });
  ```

  ```kotlin Kotlin theme={"system"}
  var response =
    client.saveRule(
      indexName = "INDEX_NAME",
      objectID = "harry-potter-rule",
      rule =
        Rule(
          objectID = "harry-potter-rule",
          conditions =
            listOf(
              Condition(
                pattern = "harry potter",
                anchoring = Anchoring.entries.first { it.value == "contains" },
              )
            ),
          consequence =
            Consequence(
              userData =
                buildJsonObject {
                  put("promo_content", JsonPrimitive("20% OFF on all Harry Potter books!"))
                }
            ),
        ),
    )
  ```

  ```php PHP theme={"system"}
  $response = $client->saveRule(
      'INDEX_NAME',
      'harry-potter-rule',
      ['objectID' => 'harry-potter-rule',
          'conditions' => [
              ['pattern' => 'harry potter',
                  'anchoring' => 'contains',
              ],
          ],
          'consequence' => ['userData' => ['promo_content' => '20% OFF on all Harry Potter books!',
          ],
          ],
      ],
  );
  ```

  ```python Python theme={"system"}
  response = client.save_rule(
      index_name="INDEX_NAME",
      object_id="harry-potter-rule",
      rule={
          "objectID": "harry-potter-rule",
          "conditions": [
              {
                  "pattern": "harry potter",
                  "anchoring": "contains",
              },
          ],
          "consequence": {
              "userData": {
                  "promo_content": "20% OFF on all Harry Potter books!",
              },
          },
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  response = client.save_rule(
    "INDEX_NAME",
    "harry-potter-rule",
    Algolia::Search::Rule.new(
      algolia_object_id: "harry-potter-rule",
      conditions: [Algolia::Search::Condition.new(pattern: "harry potter", anchoring: "contains")],
      consequence: Algolia::Search::Consequence.new(user_data: {promo_content: "20% OFF on all Harry Potter books!"})
    )
  )
  ```

  ```scala Scala theme={"system"}
  val response = Await.result(
    client.saveRule(
      indexName = "INDEX_NAME",
      objectID = "harry-potter-rule",
      rule = Rule(
        objectID = "harry-potter-rule",
        conditions = Some(
          Seq(
            Condition(
              pattern = Some("harry potter"),
              anchoring = Some(Anchoring.withName("contains"))
            )
          )
        ),
        consequence = Consequence(
          userData = Some(JObject(List(JField("promo_content", JString("20% OFF on all Harry Potter books!")))))
        )
      )
    ),
    Duration(100, "sec")
  )
  ```

  ```swift Swift theme={"system"}
  let response = try await client.saveRule(
      indexName: "INDEX_NAME",
      objectID: "harry-potter-rule",
      rule: Rule(
          objectID: "harry-potter-rule",
          conditions: [SearchCondition(pattern: "harry potter", anchoring: SearchAnchoring.contains)],
          consequence: SearchConsequence(userData: ["promo_content": "20% OFF on all Harry Potter books!"])
      )
  )
  ```
</CodeGroup>

#### Create a rule in the dashboard

To add rules in the Algolia dashboard:

1. Go to the [Algolia dashboard](https://dashboard.algolia.com/explorer/browse) and select your Algolia <Application />.

2. On the left sidebar, select <AlgoliaSearch /> **Search**.

3. Select your Algolia index.

4. Open the [**Rules**](https://dashboard.algolia.com/rules) page in the Algolia dashboard.

5. Select **Create your first rule** or **New rule**. In the drop-down menu, click the **Manual Editor** option.

6. In the **Condition(s)** section, keep **Query** toggled on, select **Contains** from the drop-down menu, and enter "harry potter" in the input field.

7. In the **Consequence(s)** section:

   1. Click **Add consequence** and select **Return Custom Data**.
   2. In the input field, add the data to return when a search query matches the rule: `{ "promo_content": "20% OFF on all Harry Potter books!" }`

8. Save your changes.

#### Duplicate rules to adapt to user search patterns

To ensure that the Samsung Galaxy Note 20 Ultra banner displays for the search term "note" in the preceding example:

1. Duplicate the existing Samsung Galaxy Note 20 Ultra rule.
2. Edit the rule to change the Query search phrase to "Note".

The newly duplicated rule now matches the first [precedence logic](/doc/guides/managing-results/rules/rules-overview/in-depth/rule-matching-algorithm) criterion (Position) and displays the desired banner.

### Retrieve the banner data in your UI

The search response includes your banner data in the `userData` property.
This data isn't displayed automatically:
your search UI must read `userData` and render the banner when it's present.
For example, with InstantSearch you can use the `queryRuleCustomData` widget:

<CodeGroup>
  ```js JavaScript theme={"system"}
  search.addWidgets([
    instantsearch.widgets.queryRuleCustomData({
      container: "#banner",
      templates: {
        default: ({ items }, { html }) =>
          items.map((item) => html`${item.promo_content}`),
      },
    }),
  ]);
  ```

  ```jsx React theme={"system"}
  import { QueryRuleCustomData } from "react-instantsearch-dom";

  const Banner = (
    <QueryRuleCustomData>
      {({ items }) =>
        items.map((item) => <p key={item.promo_content}>{item.promo_content}</p>)
      }
    </QueryRuleCustomData>
  );
  ```

  ```vue Vue theme={"system"}
  <ais-query-rule-custom-data>
    <template v-slot:item="{ item }">
      <p>{{ item.promo_content }}</p>
    </template>
  </ais-query-rule-custom-data>
  ```

  ```ios iOS theme={"system"}
  struct Banner: Decodable {
    let title: String
  }

  class BannerViewController: UIViewController, ItemController {
    
    let titleLabel: UILabel = .init()
    
    override func viewDidLoad() {
      super.viewDidLoad()
      // some layout code
    }
    
    func setItem(_ item: Banner?) {
      titleLabel.text = item?.title
    }
    
  }

  func setupWidget() {
    let searcher = HitsSearcher(appID: "undefined",
                                apiKey: "undefined",
                                indexName: "YourIndexName")
    let bannerViewController = BannerViewController()
    let queryRuleCustomDataConnector = QueryRuleCustomDataConnector<Banner>(searcher: searcher,
                                                                            controller: bannerViewController)

    searcher.search()
  }
  ```

  ```kotlin Android theme={"system"}
  @Serializable
  data class Banner(val title: String)

  class MyActivity: AppCompatActivity() {

    val searcher = HitsSearcher(
        applicationID = "YourApplicationID",
        apiKey = "YourAPIKey",
        indexName = "YourIndexName"
    )
      val searchBox = SearchBoxConnector(searcher, searchMode = SearchMode.AsYouType)
      val queryRuleCustomData = QueryRuleCustomDataConnector<Banner>(searcher)
      val connection = ConnectionHandler(searchBox, queryRuleCustomData)

      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)

          val bannerView = TextView(this)
          queryRuleCustomData.subscribe { banner ->
              bannerView.text = banner?.title
          }

          searcher.searchAsync()
      }

      override fun onDestroy() {
          super.onDestroy()
          searcher.cancel()
          connection.clear()
      }
  }
  ```
</CodeGroup>

For more information, see the InstantSearch API reference:

* InstantSearch.js: [`queryRuleCustomData`](/doc/api-reference/widgets/query-rule-custom-data/js)
* React InstantSearch: [`QueryRulesCustomData`](/doc/api-reference/widgets/query-rule-custom-data/react)
* Vue InstantSearch: [`ais-query-rule-custom-data`](/doc/api-reference/widgets/query-rule-custom-data/vue)

### Apply rules with context conditions

If you want to apply rules based on context, see:

* InstantSearch.js: [`queryRuleContext`](/doc/api-reference/widgets/query-rule-context/js)
* React InstantSearch: [`QueryRuleContext`](/doc/api-reference/widgets/query-rule-context/react)
* Vue InstantSearch: [`ais-query-rule-context`](/doc/api-reference/widgets/query-rule-context/vue)

## Determine priority when you have multiple banners

You may have several [semi-permanent promotional banners](/doc/guides/managing-results/rules/merchandising-and-promoting/how-to/merchandising-category-pages#example-display-banners-on-category-pages) for
specific [product categories](/doc/guides/solutions/ecommerce/browse/tutorials/category-pages),
as well as some time-limited, temporary banners for certain products.

For instance, an online IT hardware store has banners for the following categories:

* Phones
* Tablet computers
* Laptop computers
* Desktop computers

They also have temporary offers and associated banners for the phrases and products:

* Infinix Note 40 Pro
* Xiaomi Redmi Note 14 Pro+
* Samsung Galaxy S25 Ultra
* Samsung Galaxy Tab S10 Ultra
* Samsung Galaxy Book3 360

To decide which banner to show, Algolia uses a [tie-breaking algorithm](/doc/guides/managing-results/rules/rules-overview/in-depth/rule-matching-algorithm) when multiple rules match.

Using the preceding list of products as an example:

1. User starts on the Phones category page and sees the Phones banner.
2. They search for `note`.
3. At this point, three rules can match: the Phones category banner and the two product banners for **Infinix Note 40 Pro** and **Xiaomi Redmi Note 14 Pro+**.
4. The two product banners are more specific than the category banner, so they take precedence over it.
5. When multiple rules match, Algolia compares their `objectID` values and shows the banner whose `objectID` comes first alphabetically.

<Note>
  In this context, `objectID` refers to the rule's ID, not to any product or record `objectID` in your index.
  Rule objectIDs are unique identifiers for rules.
  When using the API, you can set them manually.
  In the dashboard, they're generated automatically.

  To control this tie-breaker, set or change the rule's `objectID` with the [`saveRule`](/doc/libraries/sdk/methods/search/save-rule) method.
</Note>
