Skip to content

Search

Site search tracking is one of the most undervalued analytics implementations. Search queries tell you exactly what users are looking for — in their own words. Zero-result queries are a product roadmap. High-volume searches for out-of-stock products are revenue leaking out of your catalog.

GA4 has a recommended search event. Use it.

Fire search when:

  • The user submits a search query and results load
  • Autocomplete suggestions are shown (optional — see below)
  • Search filters are changed on a results page

Fire once per search action, not once per character typed. If your search has a submit button, fire on submit. If it triggers on Enter key, fire on keypress. If it’s an instant-search that queries on every keystroke, fire after a debounce — not on every character change.

// User searches for "leather jacket"
dataLayer.push({
event: 'search',
search_term: 'leather jacket'
});

Extended search event with additional context

Section titled “Extended search event with additional context”
dataLayer.push({
event: 'search',
search_term: 'leather jacket',
search_results_count: 24, // number of results returned
search_type: 'full_text', // full_text, autocomplete, filter_change
search_category: 'all', // which category was searched within
search_filters_applied: false, // were filters pre-applied?
search_sort: 'relevance' // how results are sorted
});
Event Schema search
Parameter Type Required Description
event string Required Must be "search"
search_term string Required The query string the user typed. This is the key parameter — always include it.
search_results_count number Optional Number of results returned. Enables zero-result query analysis.
search_type string Optional Type of search: full_text, autocomplete, barcode_scan, voice.
search_category string Optional Category context if the search was scoped to a category.
search_filters_applied boolean Optional Whether any filters were applied to the search.
search_sort string Optional Current sort order: relevance, price_asc, price_desc, newest.

Zero-result searches are your most valuable search data — they reveal what users want that you don’t have. Track them explicitly.

// Zero results returned
dataLayer.push({
event: 'search',
search_term: 'waterproof leather jacket',
search_results_count: 0,
search_type: 'full_text'
});

In GA4, create a custom report or exploration that filters search events where search_results_count = 0. Group by search_term. This is your product gap analysis.

When a user applies or changes filters on a search results page, fire another search event to capture the refined query context.

// User applies a "Sale Items" filter on search results
dataLayer.push({
event: 'search',
search_term: 'leather jacket', // original query — preserve it
search_results_count: 8,
search_type: 'filter_change',
search_filters_applied: true,
active_filters: 'on_sale,brand:Heritage Co.' // optional: which filters
});

If you have an autocomplete/typeahead feature, consider tracking when users select a suggestion vs. type and submit manually. This helps you evaluate how useful your autocomplete is.

// User selects an autocomplete suggestion
dataLayer.push({
event: 'search',
search_term: 'leather jacket', // the selected suggestion
search_type: 'autocomplete',
search_results_count: 24
});
const searchForm = document.querySelector('#search-form');
const resultsContainer = document.querySelector('#search-results');
searchForm.addEventListener('submit', function(e) {
const query = this.querySelector('input[name="q"]').value.trim();
if (!query) return;
// Fire immediately on submit — results count not yet known
dataLayer.push({
event: 'search',
search_term: query
});
// Or wait for results and fire with count:
fetchSearchResults(query).then(results => {
dataLayer.push({
event: 'search',
search_term: query,
search_results_count: results.total
});
});
});
function SearchPage({ query, results }: { query: string; results: SearchResults }) {
useEffect(() => {
if (!query) return;
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'search',
search_term: query,
search_results_count: results.total,
search_type: 'full_text'
});
}, [query]); // Re-fires when query changes
return (/* ... */);
}
  1. Create a Custom Event trigger for search.
  2. Create Data Layer Variables for search_term, search_results_count, and any other parameters you push.
  3. Create a GA4 Event tag for search with search_term mapped as an event parameter. In GA4, register search_term as a custom dimension if you want to filter reports by it.

Not stripping whitespace from search terms. Leading and trailing spaces create spurious variations. 'leather jacket' and ' leather jacket' are different dimension values in GA4 even though they’re the same search intent. Trim search terms before pushing.

Using different casing. 'Leather Jacket' and 'leather jacket' are different values. Normalize to lowercase unless case is semantically meaningful.

Firing on every keystroke. Instant-search products fire as users type. Debounce with a 500–800ms delay before pushing, or fire only on actual result-load events.