Menu di 36 Navigasi dan Menu buat yang mau jelajah, filter di 37 Filtering buat yang mau nyempitin. Pencarian buat yang udah tau mau cari apa dan gak mau klik-klik dulu.
Ada dua lapisan pencarian di theme Shopify:
- Storefront search, pencarian “beneran” yang ngebawa pembeli ke halaman
/searchberisi hasil lengkap. Dikendaliin lewat query parameter di URL. - Predictive search, saran yang muncul sambil ngetik di kotak pencarian, tanpa pindah halaman. Dikendaliin lewat Predictive Search API (Ajax).
Dua-duanya bisa disetel merchant lewat app Search & Discovery (Search Settings), yang bisa ngubah nilai default beberapa parameter di bawah.
Storefront Search
1. Query Parameter
Pencarian nerima parameter ini di URL:
q(wajib), kata kuncinya.type, tipe hasil yang diminta, dipisah koma:product,page,article. Default semua tipe (bisa diubah di Search Settings).page, halaman hasil ke berapa, default1.options[unavailable_products], produk/varian yang gak tersedia mau diapain:show(tampil sesuai urutan ditemukan),hide(dibuang),last(ditaruh paling akhir). Defaultlast.options[prefix], pencocokan sebagian kata di term terakhir:last(default, “winter snow” nemuin “snow”, “snowshoe”, “snowboard”) ataunone.sort_by:relevance(default),price-ascending,price-descending. Buat dua yang terakhir, semua hasil non-produk didorong ke akhir.
Kenapa prefix penting: tanpa itu, pembeli yang ngetik “sepat” gak nemu “sepatu”. Dan kenapa cuma term terakhir? Karena term sebelumnya dianggap udah selesai diketik.
2. Form Pencarian
Form-nya elemen <form> dengan action ke halaman search. Selalu pakai routes.search_url, jangan hardcode /search, biar toko multi bahasa dapat URL yang bener (/fr/search). Tiap parameter jadi input dengan name parameter dan value-nya. Selain q, semua parameter gak butuh input pembeli, jadi jadiin hidden input:
<form action="{{ routes.search_url }}">
<input type="text"
placeholder="Search"
name="q"
value="{{ search.terms | escape }}"
/>
<input type="hidden" name="type" value="product,page" />
<input type="hidden" name="options[unavailable_products]" value="hide" />
<input type="hidden" name="options[prefix]" value="last" />
<input type="submit" value="Search" />
</form>Dua detail:
value="{{ search.terms | escape }}", biar kata yang dicari tetap ada di kotak setelah pencarian (pedoman UX di bawah), danescapebiar tanda kutip di kata kunci gak ngerusak HTML.- Nilai hidden input bisa dijadiin theme setting (misalnya checkbox “Sembunyiin produk habis dari hasil pencarian”) biar merchant yang nentuin.
❌ hardcode, rusak di toko multi bahasa
<form action="/search">✅ dari routes
<form action="{{ routes.search_url }}">3. Struktur URL
Waktu form disubmit, URL halaman search mencerminkan parameternya. Pencarian snow dengan type=product,page, unavailable_products=hide, prefix=last:
/search?q=snow&type=product,page&options[unavailable_products]=hide&options[prefix]=lastIni yang bikin hasil pencarian bisa dibagiin dan tombol back jalan, prinsip yang sama dengan URL filter.
4. Halaman Hasil
Di template search, object search dari 17 Objects Lainnya > search nyediain performed, terms, results (campuran produk/artikel/halaman dengan object_type), results_count, types, filters, dan sort_by/sort_options. Pola dasarnya udah ada di sana, yang perlu diingat:
- Cek
search.performeddulu. Kalaufalse(buka/searchtanpaq), tampilin form-nya aja. - Wajib
paginatesearch.results, maksimal 50 per halaman. case item.object_typebuat ngerender kartu yang beda per tipe.- Filter
highlightdari 19 Filters HTML, URL, Media, Color, dan Font > Filter Pencarian buat nyorot kata kunci di deskripsi. - Form filter hasil pencarian (dengan hidden input
q) dari 37 Filtering > 6. Form Filter Hasil Pencarian, ingat filter aktif ngebuang hasil non-produk.
Implementasi lengkapnya bisa dilihat di main-search Dawn.
Predictive Search
Saran muncul langsung waktu ngetik: produk, koleksi, query (saran kata kunci), halaman, dan artikel. Manfaatnya: pembeli bisa merumuskan dan memperhalus pencariannya, nemuin cara baru buat jelajah toko, dan langsung lihat kecocokan tanpa ninggalin halaman.
1. Gimana Saran Dibikin
Begitu pembeli ngetik, predictive search nyari yang cocok, persis atau dengan toleransi typo, di properti yang bisa dicari. Beberapa aturan:
- Kata yang dipisah tanda hubung atau spasi dianggap dua term.
T-shirtdant shirtngasih hasil sama,tshirtbeda. - Ketik
very-fast snowbo→ produk yang mengandungvery,fast, dan term yang diawalisnowbo. - Varian cuma dikembaliin kalau query cocok sama term spesifik judul varian, dan cuma varian dengan term cocok terbanyak. Cari
snowbo→ produk snowboard. Carilight blue snowbo→ cuma varian light blue. Waktu varian yang cocok,featured_media/imagedanurlproduk ngikutin varian itu. - Query suggestion (saran kata kunci) dihasilin dengan ngekstrak kata dan frasa dari katalog produk dan pencarian pembeli pakai NLP. Tapi cuma bahasa Inggris, dan butuh locale toko dan locale pembeli sama-sama Inggris.
2. Tiga Komponen Implementasi
- Main search, input pencarian tempat predictive search nempel.
- Section predictive search, section yang ngerender saran dari
predictive_search.resources. - Fungsi JavaScript, yang ngambil section itu lewat section response Predictive Search API dan nyuntikinnya ke halaman.
Ini pola yang sama dengan product recommendations di 41 Produk Recommendations, Gift Card, dan Bundles: object Liquid-nya cuma terisi kalau section dirender lewat API, jadi kamu butuh JavaScript buat “manggil” section-nya. Contoh di sini ngikutin pola listbox dari W3C ARIA Authoring Practices. Implementasi lengkapnya di Dawn: sections/main-search.liquid, sections/predictive-search.liquid, assets/predictive-search.js.
3. Main Search
<script src="{{ 'predictive-search.js' | asset_url }}" defer="defer"></script>
<predictive-search>
<form action="{{ routes.search_url }}" method="get" role="search">
<label for="Search">Search</label>
<input
id="Search"
type="search"
name="q"
value="{{ search.terms | escape }}"
role="combobox"
aria-expanded="false"
aria-owns="predictive-search-results"
aria-controls="predictive-search-results"
aria-haspopup="listbox"
aria-autocomplete="list"
/>
<input name="options[prefix]" type="hidden" value="last" />
<div id="predictive-search" tabindex="-1"></div>
</form>
</predictive-search>Yang penting:
- Dibungkus custom element
<predictive-search>yang dibikin di JavaScript (web component, pola yang sama dengan 35 Developer Preview tag {% block %} dan {% partial %}). - Form-nya tetap form biasa ke
routes.search_url. Kalau JavaScript gagal, Enter tetap ngebawa ke halaman hasil. Progressive enhancement. - Atribut ARIA
role="combobox",aria-expanded,aria-controls,aria-haspopup="listbox",aria-autocomplete="list"nandain ke screen reader kalau input ini punya daftar saran. <div id="predictive-search">itu wadah kosong yang bakal diisi hasil section.
4. Section Predictive Search
Ngerender predictive_search.resources dari 17 Objects Lainnya > predictive_search. Contoh ini cuma produk, Dawn ngerender semua tipe:
{%- if predictive_search.performed -%}
<div id="predictive-search-results">
{%- if predictive_search.resources.products.size > 0 -%}
<h3 id="predictive-search-products">
Products
</h3>
<ul role="listbox" aria-labelledby="predictive-search-products">
{%- for product in predictive_search.resources.products -%}
<li role="option">
<a href="{{ product.url }}">
{{ product | image_url: width: 50 | image_tag }}
<span>{{ product.title }}</span>
</a>
</li>
{%- endfor -%}
</ul>
{%- endif -%}
<button>
Search for “{{ predictive_search.terms }}”
</button>
</div>
{%- endif -%}predictive_search.performed false waktu section dirender normal (misalnya kalau kamu iseng naruh section ini di template), jadi gak ngeluarin apa-apa. Cuma true waktu dirender lewat API. Tombol “Search for …” di bawah itu link “lihat semua hasil” yang diwajibin pedoman UX.
5. Fungsi JavaScript
class PredictiveSearch extends HTMLElement {
constructor() {
super();
this.input = this.querySelector('input[type="search"]');
this.predictiveSearchResults = this.querySelector('#predictive-search');
this.input.addEventListener('input', this.debounce((event) => {
this.onChange(event);
}, 300).bind(this));
}
onChange() {
const searchTerm = this.input.value.trim();
if (!searchTerm.length) {
this.close();
return;
}
this.getSearchResults(searchTerm);
}
getSearchResults(searchTerm) {
fetch(`/search/suggest?q=${searchTerm}§ion_id=predictive-search`)
.then((response) => {
if (!response.ok) {
var error = new Error(response.status);
this.close();
throw error;
}
return response.text();
})
.then((text) => {
const resultsMarkup = new DOMParser().parseFromString(text, 'text/html').querySelector('#shopify-section-predictive-search').innerHTML;
this.predictiveSearchResults.innerHTML = resultsMarkup;
this.open();
})
.catch((error) => {
this.close();
throw error;
});
}
open() {
this.predictiveSearchResults.style.display = 'block';
}
close() {
this.predictiveSearchResults.style.display = 'none';
}
debounce(fn, wait) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn.apply(this, args), wait);
};
}
}
customElements.define('predictive-search', PredictiveSearch);Alurnya:
- Debounce 300ms di event
input, biar gak nembak API tiap huruf. Ini penting karena API punya throttle (429 kalau kebanyakan). - Kalau kosong, tutup. Kalau ada,
fetchke/search/suggestdenganqdansection_id=predictive-search(nama file section-nya). - Respons berupa HTML section yang udah dirender dengan
predictive_searchterisi. Parse pakaiDOMParser, ambil isi#shopify-section-predictive-search(ID pembungkus section, formatshopify-section-<id>dari 26 Sections dan Section Schema), suntik ke wadah.
Contoh ini hardcode /search/suggest. Yang disarankan: pakai routes.predictive_search_url dari Liquid (dilempar ke JavaScript lewat atribut data atau window.Shopify.routes.root) biar URL-nya sadar locale.
❌ hardcode, salah di /fr/
fetch(`/search/suggest?q=${searchTerm}§ion_id=predictive-search`)✅ sadar locale
fetch(`${window.Shopify.routes.root}search/suggest?q=${searchTerm}§ion_id=predictive-search`)Predictive Search API
Detail endpoint-nya, bagian dari Ajax API yang dibahas umum di 51 Ajax API dan Section Rendering API. Semua request harus pakai URL sadar locale.
1. GET /{locale}/search/suggest.json
Ngembaliin JSON mentah. Buat theme yang mau ngerender saran sendiri di JavaScript (jarang, karena section response lebih praktis). Parameternya:
q(wajib).resources[type]:product,page,article,collection,query, dipisah koma. Defaultquery,product,collection,page(bisa diubah di Search Settings).resources[limit]: 1 sampai 10, default 10.resources[limit_scope]:all(default, totallimitdi semua tipe) ataueach(limitper tipe).resources[options][unavailable_products]:show,hide,last(default), cuma buatproduct.resources[options][fields]: field yang dicari:author,body,product_type,tag,title,variants.barcode,variants.sku,variants.title,vendor. Defaulttitle,product_type,variants.title,vendor, dan Shopify nyaranin tetap pakai default buat pengalaman terbaik.
fetch(window.Shopify.routes.root + "search/suggest.json?q=bag&resources[type]=product&resources[options][unavailable_products]=hide&resources[options][fields]=title,product_type,variants.title")
.then((response) => response.json())
.then((suggestions) => {
const productSuggestions = suggestions.resources.results.products;
if (productSuggestions.length > 0) {
const firstProductSuggestion = productSuggestions[0];
alert(`The title of the first product suggestion is: ${
firstProductSuggestion.title}`
);
}
}
);Responsnya resources.results dengan array queries, products, collections, pages, articles. Object produknya punya available, body, compare_at_price_min/max, handle, id, image, price, price_min/max, tags, title, type, url, variants (dengan featured_image), vendor, featured_image. Query object punya url, text, dan styled_text (bagian yang disarankan udah ditebelin). Perhatiin url produk bawa parameter _pos, _psq, _ss, _v buat analitik pencarian.
Hati-hati: jangan output body di toko multi bahasa, karena isinya gabungan semua terjemahan.
2. GET /{locale}/search/suggest
Ngembaliin HTML section yang dirender dengan predictive_search terisi. Parameter sama dengan .json plus section_id (wajib). Ini yang dipakai contoh JavaScript di atas, dan ini cara yang disarankan karena markup-nya ditulis di Liquid, satu sumber kebenaran buat tampilan.
const predictiveSearchSection = document.querySelector('.predictive-search-results');
var requestResponse;
fetch(window.Shopify.routes.root + "search/suggest?q=bag&resources[type]=product&resources[options][unavailable_products]=hide&resources[options][fields]=title,product_type,variants.title§ion_id=predictive-search")
.then((response) => {
requestResponse = response;
return response.text();
})
.then((text) => {
if (!requestResponse.ok) {
throw new Error(`${requestResponse.status}: ${text}`);
}
const resultsMarkup = new DOMParser()
.parseFromString(text, 'text/html')
.querySelector('#shopify-section-predictive-search').innerHTML;
predictiveSearchSection.innerHTML = resultsMarkup;
})
.catch((error) => {
console.error(error);
});Section ini:
{%- if predictive_search.performed -%}
<div id="predictive-search-results">
{%- if predictive_search.resources.products.size > 0 -%}
<h3>Products</h3>
<ul>
{%- for product in predictive_search.resources.products -%}
<li><a href="{{ product.url }}">{{ product.title }}</a></li>
{%- endfor -%}
</ul>
{%- endif -%}
</div>
{%- endif -%}Ngasilin respons:
<div id="shopify-section-predictive-search">
<div id="predictive-search-results">
<h3>Products</h3>
<ul>
<li><a href="/products/running-shoes">Running Shoes</a></li>
<li><a href="/products/tennis-shoes">Tennis Shoes</a></li>
</ul>
</div>
</div>3. Error
- 404,
section_idgak ketemu di theme. - 417 Expectation Failed, bahasa pembeli gak didukung (
"Unsupported buyer locale"). - 422 Invalid parameter, nilai parameter salah,
description-nya ngejelasin. - 429 Too many requests, kena throttle, ada header
Retry-Afterdalam detik. - Lainnya 5xx.
Makanya contoh JavaScript-nya ngecek response.ok dan nutup dropdown kalau error, jangan sampai pembeli lihat HTML error di dropdown.
4. Properti yang Dicari
- Produk:
body,product_type,tag,title,variants.barcode,variants.sku,variants.title,vendor. - Halaman:
author,body,title. - Artikel:
author,body,tag,title. - Koleksi:
titleaja.
Di storefront yang diterjemahin, yang bisa dicari lebih sedikit: produk body, title, variants.title; halaman dan artikel body, title. Dan saran koleksi cuma berdasarkan bahasa utama toko, gak ngecek terjemahan koleksi.
5. Typo Tolerance dan Partial Match
Typo tolerance = 1: hasil yang beda satu huruf, atau dua huruf tertukar, tetap ketemu. Syaratnya 4 huruf pertama harus bener. Berlaku di produk (title, product_type, variants.title, vendor), halaman dan artikel (author, title), koleksi (title).
Partial word match: sweate → sweater. Batasannya: cuma di term terakhir kalau query lebih dari satu term, cuma di akhir kata (book gak nemuin ebook), dan cuma buat bahasa yang didukung. Predictive search pakai mesin pencari yang beda dari storefront search, jadi partial match-nya beda: storefront search cuma partial match kalau options[prefix]=last.
6. Bahasa yang Didukung
Predictive search jalan kalau locale pembeli salah satu dari: Afrikaans, Albania, Armenia, Bosnia, Bulgaria, Katalan, Kroasia, Ceko, Denmark, Belanda, Inggris, Estonia, Faroe, Finlandia, Prancis, Gaelik, Jerman, Yunani, Hungaria, Islandia, Indonesia, Italia, Latin, Latvia, Lituania, Makedonia, Moldova, Norwegia (Bokmål dan Nynorsk), Polandia, Portugis (Brasil dan Portugal), Rumania, Rusia, Serbia, Serbo-Kroasia, Slovakia, Slovenia, Spanyol, Swedia, Turki, Ukraina, Vietnam, Welsh.
Cara ngecek dari JavaScript: di <head> ada <script id="shopify-features"> berisi JSON dengan kunci predictiveSearch true/false. Cek ini sebelum nyalain fitur, biar pembeli berbahasa Jepang (gak ada di daftar) gak dapat dropdown yang selalu error 417.
7. Batasan Lain
- Produk individual gak bisa dikecualiin dari saran, kecuali disembunyiin dari mesin pencari lewat metafield
seo.hidden(dibahas di 48 SEO Metadata, hreflang, robots.txt). - Maksimal 10 saran per tipe per request.
- Query suggestion cuma Inggris.
Pedoman UX Predictive Search
1. Persyaratan UI
- Jumlah hasil: API default maksimal 10 di semua tipe, jadi atur campurannya (
limit_scope=eachkalau mau per tipe). Batasi jumlah saran biar gak kebanyakan, dan tiap tipe diberi label atau gaya berbeda. - Tombol tutup (ikon atau teks) yang keluar dari pengalaman pencarian dan nutup dropdown, tanpa ngehapus kata kunci.
- Tombol cari yang ngebawa ke halaman hasil, juga bisa lewat Enter atau “go”/“search” di keyboard mobile.
- Kata kunci tetap di kotak sampai pembeli ngehapusnya sendiri atau pindah halaman, dan diulang di halaman hasil.
- Empty state: sembunyiin dropdown kalau gak ada saran, biar pembeli lanjut ke halaman hasil.
- Mobile: fokusin input waktu ikon search diketuk, dan jangan ganggu scroll di dalam dropdown.
- Desktop: jangan sembunyiin info di balik scroll atau tinggi tetap, biarin dropdown nyesuain tinggi kontennya.
2. Aksesibilitas
Matiin autocomplete browser, riwayat pencarian, dan autocorrect/autocomplete OS mobile (autocomplete="off", autocorrect="off", autocapitalize="off", spellcheck="false"), karena overlay-nya bertabrakan sama dropdown kamu dan logika autocomplete-nya bersaing.
3. Praktik Terbaik
- Info seminimal mungkin yang cukup buat milih: query →
styled_text; produk → gambar, judul, harga; koleksi, artikel, halaman → judul aja. - Query suggestion: sorot bagian yang disarankan, bukan yang diketik. Pembeli ngetik “card”, saran “cardigan” ditampilin sebagai cardigan, bukan cardigan. Lebih gampang dipindai.
- Label: “Sold out” cuma kalau seluruh produk gak tersedia. Pakai heading (“Products”) biar pembeli tau tipe hasilnya.
- Tombol clear di kotak pencarian.
- Visibilitas: kotak atau minimal ikon search di header semua halaman. Toko dengan sedikit produk boleh naruhnya di menu hamburger.
- Gelapin latar halaman waktu dropdown aktif biar fokus.
- Link “more results”: “Search for [query]” atau “View all results” di daftar saran, karena hasil lengkapnya mungkin lebih banyak.
4. Pola Layout
- Inline search (desktop dan mobile): kotak pencarian kelihatan di header, dropdown muncul di bawahnya dan menimpa halaman.
- In-menu search: gak ada kotak di header, pencarian ada di dalam menu, saran muncul di bawah kotak di dalam menu (atau kotak di atas menu dengan saran menimpa daftar menu).
Ringkasan
- Storefront search dikendaliin query parameter:
q,type(product/page/article),page,options[unavailable_products](show/hide/last),options[prefix](last/none),sort_by(relevance/price-ascending/price-descending). - Form pencarian:
action="{{ routes.search_url }}", inputqdenganvalue="{{ search.terms | escape }}", parameter lain sebagai hidden input. - Halaman hasil: cek
search.performed,paginatemaksimal 50,case object_type,highlight, form filter dengan hiddenq. - Predictive search = main search (form biasa + ARIA combobox + wadah kosong) + section yang ngerender
predictive_search.resources(cuma terisi lewat API) + JavaScript yang fetch/search/suggest?section_id=...dengan debounce dan nyuntik#shopify-section-<id>. - Dua endpoint:
suggest.json(JSON mentah) dansuggest(HTML section, disarankan), parameterresources[type],resources[limit](1–10),resources[limit_scope](all/each),resources[options][unavailable_products],resources[options][fields]. Selalu URL sadar locale. - Error: 404 section, 417 bahasa, 422 parameter, 429 throttle (
Retry-After). - Pencocokan: typo tolerance 1 (4 huruf pertama bener), partial match cuma term terakhir dan akhir kata, varian cuma kalau judul varian cocok,
T-shirt=t shirt≠tshirt. - Bahasa didukung termasuk Indonesia, cek
predictiveSearchdi#shopify-features. Query suggestion cuma Inggris, koleksi cuma bahasa utama, maksimal 10 per tipe, produk cuma bisa disembunyiin lewatseo.hidden. - UX: tombol tutup dan cari, kata kunci dipertahankan, sembunyiin empty state, matiin autocomplete browser, info minimal, sorot bagian saran, link “search for”, gelapin latar.
Bab selanjutnya kita masuk ke bagian produk: cara nampilin dan milih varian (dropdown, swatch, radio), update harga dan ketersediaan waktu varian berubah, deep link varian, dan penanganan produk dengan varian sangat banyak (sampai 2.048).