Advanced Features & Customization
SearchJet gives store owners and developers full control over the search experience. This page covers the Shopify Liquid drop, building your own search box in a theme, custom result templates, and hooking into search events, including click tracking.
Using the Liquid drop
SearchJet provides a Liquid drop object that theme developers can use to access search data directly in Shopify templates:
{{ searchjet.results_count }}
{{ searchjet.query }}
Theme integration (manual)
For full control, add a search box that calls the SearchJet REST API directly. There's no script to load from a CDN. Paste the snippet below into a theme section, snippet or page template.
Use your public search key (the "Public Key" on the dashboard's Sites list). It's read-only, so it's safe in theme code. Never put the private key in a theme. See API Key Setup.
Search runs straight from the visitor's browser. That matters because the search endpoint allows 30 requests per minute per client IP: every visitor gets their own budget, which they wouldn't if requests went through your server. Style #searchjet-search with your theme's CSS, using its existing colour variables, so the box matches light and dark themes.
To narrow results, add a filters parameter, for example filters: 'type = "product"'. On Free you can filter on id, title, type, price, currency and sku. Paid plans can filter on any attribute marked filterable in Index Settings – General Attributes. See Building Search Queries.
Custom result templates
You render the results yourself, so resultTemplate is your template. Replace it to match your store's design. Check hit.type to render each kind of result differently.
Which fields you can use depends on your plan:
- Free:
id,title,url,type,thumbnail,price,currencyandsku. There's no excerpt. - Pro: adds
excerpt,content,author,date,image,categories,tags,sale_price,regular_priceandstock_status. - Growth and Enterprise: every field you indexed.
function resultTemplate(hit, position) {
const link = (inner) =>
`${inner}`;
if (hit.type === 'product') {
return `${link(`
${hit.thumbnail ? `
` : ''}
${escapeHtml(hit.title)}
${hit.price != null ? `${escapeHtml(hit.price)} ${escapeHtml(hit.currency || '')}` : ''}
${hit.stock_status === 'outofstock' ? 'Sold out' : ''}
`)} `;
}
if (hit.type === 'post') {
return `${link(`
${escapeHtml(hit.title)}
${hit.author || hit.date ? `${escapeHtml(hit.author || '')} ${escapeHtml(hit.date || '')}` : ''}
${hit.excerpt ? `${escapeHtml(hit.excerpt)}` : ''}
`)} `;
}
return `${link(escapeHtml(hit.title))} `;
}
A field shows up only if you included it when indexing and your plan returns it. Always escape values before inserting them into HTML. On Pro and above, you can also request highlighted text with attributesToHighlight and read it from hit._formatted.
Event callbacks
The search box above dispatches DOM events (searchjet:search, searchjet:zero-results and searchjet:result-click), so other scripts can react without you editing the search code:
document.addEventListener('searchjet:zero-results', (e) => {
console.log('No results for', e.detail.query);
});
Sending clicks to SearchJet analytics
POST /api/v1/analytics/click records result clicks, but it needs the private key, so the browser can't call it directly. Send the click to an endpoint on your own server, and forward it from there:
// Browser: send to your own backend
document.addEventListener('searchjet:result-click', (e) => {
navigator.sendBeacon('/searchjet-click', JSON.stringify({
document_id: e.detail.documentId,
position: e.detail.position,
}));
});
// Your server (Node.js): forward with the private key
await fetch('https://app.searchjetengine.com/api/v1/analytics/click', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SEARCHJET_PRIVATE_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ document_id: body.document_id, position: body.position }),
});
document_id and position (1 or higher) are required. session_id and user_agent are optional. Validate what the browser sends before forwarding it. You can read the results with GET /api/v1/search-analytics. See the REST API Reference.
Related pages
- Basic Usage
- Search API Deep Dive: semantic search and AI answers
- Error Handling