Skip to main content
Template helpers provide utilities for rendering custom UI in InstantSearch widgets. They support both function-based templates (JSX-like with Preact) and string-based templates (Hogan.js/Mustache).

Template Types

InstantSearch supports two template formats: Modern function-based templates using Preact and HTM:
import { html } from 'htm/preact';

const template = (data, { html, components }) => html`
  <div class="hit">
    <h3>${components.Highlight({ hit: data, attribute: 'name' })}</h3>
    <p>${data.description}</p>
  </div>
`;

String Templates (Legacy)

Mustache/Hogan.js string templates:
const template = `
  <div class="hit">
    <h3>{{name}}</h3>
    <p>{{description}}</p>
  </div>
`;

renderTemplate

Renders a template with data and helpers.
import { renderTemplate } from 'instantsearch.js/es/lib/templating';

const html = renderTemplate({
  templates: {
    item: (data) => `<div>${data.name}</div>`,
  },
  templateKey: 'item',
  data: { name: 'Product' },
});

Parameters

templates
object
required
Object containing all available templates.
{
  item: (hit) => html`<div>${hit.name}</div>`,
  empty: () => html`<p>No results</p>`,
}
templateKey
string
required
The key of the template to render.
templateKey: 'item'
data
object
Data to pass to the template.
data: {
  name: 'iPhone 13',
  price: 999,
}
compileOptions
object
Options for Hogan.js compilation (string templates only).
compileOptions: {
  delimiters: '<% %>',
}
helpers
object
Custom helper functions for string templates.
helpers: {
  formatPrice: (value, render) => {
    return '$' + render(value);
  },
}
bindEvent
object
Event binding utilities for click tracking.
bindEvent: {
  createClickEvent(event, hit) {
    return { type: 'click', hit };
  },
}
sendEvent
Function
Function to send analytics events.
sendEvent: (eventType, hit, eventName) => {
  console.log('Event:', eventType, hit, eventName);
}

Template Components

Function templates have access to built-in components:

Highlight

Highlights matching query terms in a hit attribute.
const template = (hit, { html, components }) => html`
  <div>
    ${components.Highlight({ hit, attribute: 'name' })}
  </div>
`;
hit
object
required
The search result hit object.
attribute
string
required
The attribute to highlight.
highlightedTagName
string
default:"'mark'"
HTML tag name for highlighted parts.

Snippet

Displays a snippet of a hit attribute with highlighted matches.
const template = (hit, { html, components }) => html`
  <div>
    ${components.Snippet({ hit, attribute: 'description' })}
  </div>
`;
hit
object
required
The search result hit object.
attribute
string
required
The attribute to snippet.
highlightedTagName
string
default:"'mark'"
HTML tag name for highlighted parts.

ReverseHighlight

Highlights non-matching parts (inverse of Highlight).
const template = (hit, { html, components }) => html`
  <div>
    ${components.ReverseHighlight({ hit, attribute: 'name' })}
  </div>
`;

ReverseSnippet

Snippets non-matching parts (inverse of Snippet).
const template = (hit, { html, components }) => html`
  <div>
    ${components.ReverseSnippet({ hit, attribute: 'description' })}
  </div>
`;

prepareTemplateProps

Prepares template properties for rendering.
import { prepareTemplateProps } from 'instantsearch.js/es/lib/templating';

const props = prepareTemplateProps({
  defaultTemplates: {
    item: (hit) => html`<div>${hit.name}</div>`,
  },
  templates: {
    item: (hit) => html`<div class="custom">${hit.name}</div>`,
  },
  templatesConfig: {},
});

Examples

Function Template with Components

import { html } from 'htm/preact';

const hitTemplate = (hit, { html, components }) => html`
  <article class="hit">
    <img src="${hit.image}" alt="${hit.name}" />
    <div class="hit-content">
      <h3>${components.Highlight({ hit, attribute: 'name' })}</h3>
      <p>${components.Snippet({ hit, attribute: 'description' })}</p>
      <span class="price">$${hit.price}</span>
    </div>
  </article>
`;

String Template with Helpers

const template = `
  <div class="product">
    <h3>{{#helpers.highlight}}name{{/helpers.highlight}}</h3>
    <p>{{#helpers.formatPrice}}price{{/helpers.formatPrice}}</p>
  </div>
`;

const helpers = {
  formatPrice: function(value, render) {
    return '$' + render(value);
  },
};

renderTemplate({
  templates: { item: template },
  templateKey: 'item',
  data: hit,
  helpers,
});

Template with Event Tracking

const hitTemplate = (hit, { html, sendEvent }) => html`
  <article 
    class="hit"
    onClick=${() => sendEvent('click', hit, 'Product Clicked')}
  >
    <h3>${hit.name}</h3>
    <button 
      onClick=${(e) => {
        e.stopPropagation();
        sendEvent('conversion', hit, 'Added to Cart');
      }}
    >
      Add to Cart
    </button>
  </article>
`;

Conditional Template

const hitTemplate = (hit, { html, components }) => html`
  <div class="hit">
    <h3>${components.Highlight({ hit, attribute: 'name' })}</h3>
    ${hit.onSale && html`<span class="badge">On Sale!</span>`}
    ${hit.image ? html`<img src="${hit.image}" />` : html`<div class="no-image">No image</div>`}
    <p class="price ${hit.onSale && 'sale-price'}">
      $${hit.price}
    </p>
  </div>
`;

Empty State Template

const templates = {
  item: (hit, { html, components }) => html`
    <div class="hit">
      ${components.Highlight({ hit, attribute: 'name' })}
    </div>
  `,
  empty: (results, { html }) => html`
    <div class="empty-state">
      <p>No results found for <strong>${results.query}</strong></p>
      <p>Try adjusting your search or filters</p>
    </div>
  `,
};

Render Multiple Attributes

const hitTemplate = (hit, { html, components }) => html`
  <article class="hit">
    <h3>${components.Highlight({ hit, attribute: 'name' })}</h3>
    <p class="brand">${components.Highlight({ hit, attribute: 'brand' })}</p>
    <div class="description">
      ${components.Snippet({ hit, attribute: 'description' })}
    </div>
    <ul class="features">
      ${hit.features?.map(feature => html`
        <li>${feature}</li>
      `)}
    </ul>
  </article>
`;

Custom Highlight Styling

const hitTemplate = (hit, { html, components }) => html`
  <div class="hit">
    ${components.Highlight({ 
      hit, 
      attribute: 'name',
      highlightedTagName: 'strong',
    })}
  </div>
`;

Legacy String Template Syntax

Variables

`<div>{{name}}</div>`

Sections (loops, conditionals)

`
  {{#items}}
    <div>{{name}}</div>
  {{/items}}
  
  {{^items}}
    <p>No items</p>
  {{/items}}
`

Helpers

`<div>{{#helpers.formatPrice}}price{{/helpers.formatPrice}}</div>`

Best Practices

Use Function Templates: Function templates with HTM/Preact provide better performance, TypeScript support, and access to components like Highlight and Snippet.
XSS Protection: Always use components like Highlight and Snippet for user-generated content. Never use raw HTML interpolation with untrusted data.
Event Tracking: Use the sendEvent function in templates to track user interactions for analytics and personalization.

Build docs developers (and LLMs) love