Guide 11 · Developer docs

Search, filtering and faceted navigation

Site search on Core dna is an Elasticsearch index that the platform fills and queries, and a page you build. What reaches the index, search views, the search page, product facets, filtered listings that need no index, and when to go beyond the built-in index.

L2
Unit 04 · Listings without the index

Where filter state belongs

On a filtered listing or a product finder the URL is the filter state. When a script also remembers a visitor's choices, state that belongs to one tab goes in sessionStorage and a preference for every tab in localStorage; mixing them up makes two tabs fight over one filter.

Who this is for. The front-end developer adding "remember my filter" or "back to the results" behaviour to a listing, a search page or a product finder. Afterwards you can choose the right place for each piece of state and avoid the mix-up that only shows when a visitor has two tabs open.

The URL is the filter state

A filter belongs in the address bar first: it is shareable and bookmarkable, it survives a reload, the back button steps through it, the server renders the page from it, and the page cache can store each combination (Facet state belongs in the URL). A script never holds a filter on its own. Browser storage only answers smaller questions around it: where should "Back to the results" lead, and should a visitor who returns to an unfiltered listing see their last filter again?

Per tab, or per visitor

The two kinds of browser storage differ in one way that decides everything here. localStorage is shared by every tab of the site and lasts until it is cleared. sessionStorage belongs to one tab: it survives reloads and navigation within that tab, and ends when the tab closes.

StateBelongs toStore it in
A list's filter, sort, page or selection; the results to go back toOne tabsessionStorage
A theme, a language, a dismissed banner, a preferred code languageThe visitor, in every tablocalStorage

The failure of getting it wrong is concrete. A visitor opens the blog in two tabs, filters the first to "news" and the second to "recipes", then opens a post in the first tab. With the last filter in localStorage both tabs share one value, so whichever filtered last wins: the first tab's "Back to the results" leads to recipes. With sessionStorage each tab keeps its own. An expiry of a day or so, sometimes added to a filter kept in localStorage, is a sign that the wrong store was chosen: the right lifetime for a tab's state is the tab.

This documentation site follows the rule: your choice between Smarty and Twig in the code samples is a preference for every tab, so it is kept in localStorage.

A "back to the results" link that each tab gets right

The listing remembers, per tab, the URL it was last viewed at, query string included; a post points its back link there when the tab has one, and keeps the plain listing as its default otherwise. Both reads are wrapped, because storage can be unavailable in a private window, and the stored value is checked to be a path on this site before it becomes a link:

JavaScript src/js/back-to-results.js
const KEY = 'last-listing:/blog';

// On the listing: remember this tab's filtered URL.
if (document.querySelector('[data-listing="blog"]')) {
  try {
    sessionStorage.setItem(KEY, location.pathname + location.search);
  } catch (e) { /* storage unavailable: the default link stays */ }
}

// On a post: point "Back to the results" at this tab's last listing.
const back = document.querySelector('[data-back-to-list="blog"]');
if (back) {
  let last = null;
  try { last = sessionStorage.getItem(KEY); } catch (e) { /* as above */ }
  if (last && last.startsWith('/') && !last.startsWith('//')) {
    back.href = last;
  }
}

The template renders the back link with the plain listing's URL as its href and the attribute data-back-to-list="blog", so it works with no script and in a tab that never saw the listing. The same pattern restores a visitor's last filter on an unfiltered visit: read the tab's value and redirect to it only when the URL has no filter of its own, so that a shared link always wins over the visitor's memory.

Go deeper

Was this page helpful?

Your answer goes to the docs team.