Hybrid Search — Results and Sources
Change how Hybrid Search lists articles: where the links open, how the dates read, the layout, and the markup of each item.
Hybrid Search lists articles in two places: the search results, and the sources that the answer cites. Below we open the links in the same tab, change the date format, show the results as cards, and then write the markup of a result, with its date added.
You need a working Hybrid Search page first. See Hybrid Search — Quick Start.
The two lists
| Element | What it lists | Fields from | Layout |
|---|---|---|---|
<miso-products> | The search results. | fl | list, with more results as the reader scrolls |
<miso-sources> | The articles that the answer cites, numbered to match the citations. | source_fl | horizontal: one row of small cards |
Each change below goes to useLayouts on client.ui.hybridSearch. The key is
the role of the list: products or sources. Your options are merged over the
defaults, so set only what you change. Set them before the template goes into
the page.
Open links in the same tab
Each item is a link to the article's url, and it opens in a new tab. To open
it in the same tab, set link.open to false:
client.ui.hybridSearch.useLayouts({
products: { link: { open: false } },
sources: { link: { open: false } },
});
link option | Default | What it does |
|---|---|---|
open | true | false leaves out target and rel, so the link opens in the same tab. |
target | "_blank" | The target attribute of the link. |
rel | "noopener" | The rel attribute of the link. |
Change the date format
The sources show the authors and the date. The date is published_at, or
created_at or updated_at when the record has no published_at. The
default format is Mar 5, 2026. Give the date template the options of
toLocaleDateString,
with locale added:
client.ui.hybridSearch.useLayouts({
sources: {
templates: {
date: { locale: "en-GB", year: "numeric", month: "long", day: "numeric" },
},
},
});
date can also be a locale string, such as "de-DE", or a function that gets
the date value from the record and returns the text.
Show the results as cards
Give the layout type before the options:
client.ui.hybridSearch.useLayouts({
products: ["cards", { link: { open: false } }],
});
| Type | What the reader sees |
|---|---|
list | One result on each row. The default. |
cards | Results side by side, in a grid. |
Write the markup of a result
A result shows its image, title and description. To draw it your own way, give
the product template. This one adds the date under the title.
Start the template with tagPair, which returns the opening and closing tag
of the item. The tags link to the article and carry the attributes that the
SDK uses to record clicks:
function renderProduct(layout, state, data, meta) {
const { tagPair, escapeHtml } = layout.templates.helpers;
const [openTag, closeTag] = tagPair(layout, data);
const image = data.cover_image
? `<img class="my-result-image" src="${escapeHtml(data.cover_image)}" alt="">`
: "";
const date = data.published_at
? new Date(data.published_at).toLocaleDateString("en-US", { dateStyle: "medium" })
: "";
return openTag +
image +
`<div class="my-result-title">${escapeHtml(data.title)}</div>` +
`<div class="my-result-date">${date}</div>` +
`<div class="my-result-description">${escapeHtml(data.description || "")}</div>` +
closeTag;
}
client.ui.hybridSearch.useLayouts({
products: { templates: { product: renderProduct } },
});
| Argument | What it holds |
|---|---|
layout | The layout. layout.templates.helpers has tagPair and escapeHtml. |
state | The state of the whole list. |
data | The record, with the fields from fl for a result, or source_fl for a source. product_id and title are always there. |
meta | meta.index is the position of the item, counted from 0. |
A field that fl does not name is undefined in the template. The default fl
is:
["cover_image", "url", "created_at", "updated_at", "published_at", "title"]
To show another field, add it with useApi. The list replaces the default, so
keep the fields that the template still uses. A custom attribute uses its
dotted path, such as custom_attributes.section:
client.ui.hybridSearch.useApi({
fl: ["cover_image", "url", "published_at", "title", "description", "categories"],
});
For a source, give the article template under sources in the same way. Add
<span class="miso-citation-index" data-index="${meta.index + 1}"></span> to
it. That element draws the number that the citations in the answer point to.
Keep these in mind when you write one:
- Escape every value from the record with
escapeHtml. - Format the date yourself. The
dateoption applies to the default markup only. - Style your own classes in your CSS.
If it does not work
| Symptom | Cause |
|---|---|
A field shows undefined | It is not in fl for a result, or in source_fl for a source. |
| The SDK stops recording clicks on the items | The product or article template does not use tagPair. |
| The sources have no numbers | The article template has no miso-citation-index element. |
Next
- UI Customization — change the headings, the parts and the order of the page.
- Facets — the category filters next to the results.
- Citation Links — the class, the attributes and the tooltip of each citation.
