Appearance
Search support guide
Diagnostic and troubleshooting reference for predictive search (Storefront API GraphQL backend with typo tolerance, REST fallback when no token), full search results page with typed tabs (Products / Articles / Pages), and the empty-state pack (did-you-mean, recent searches, popular searches, suggested collections).
For merchant setup and settings configuration, see the Search guide.
FAQ
Q1: Predictive search shows no results at all
Check in order:
Is the Storefront Access Token configured? Open DevTools → Console → paste the console diagnostic snippet. Look for
token: true. Iftoken: false, the token field in Theme Settings → Storefront API is blank. Follow the token setup steps.Is the REST fallback working? If
token: falsebutpredictiveSearchBackend: "rest", the fallback is active. Verify the store has published products by searching for a known product title. If results still don't appear, check that the store has published products visible in Shopify admin → Products.Is the search drawer open? The
<predictive-search>Custom Element only fetches after the drawer is open AND the input has at least 2 characters. The script that registers it lazy-loads on first focus/input on the search field — focus the input first, then verifypredictiveCE: truein the diagnostic snippet.Network error? Open DevTools → Network → type in the search input → look for a POST to
/api/2026-04/graphql.json(Storefront API) or a GET to/search/suggest.json(REST fallback). If the request returns 401 or 403, the token may be revoked or incorrect.
Q2: Typo tolerance not working (e.g., "shrt" does not return shirt results)
Typo tolerance requires the Storefront API token. Check:
window.uisceShopConfig.predictiveSearchBackendin the console diagnostic. If"rest", you are on the REST fallback — typo tolerance is not available via/search/suggest.json.If
predictiveSearchBackend: "graphql"but typo results are missing, verify the Storefront API version inwindow.uisceShopConfig.storefrontApiVersion— it should be2026-04or later. Earlier versions may have different typo-tolerance behavior.Some very short tokens (3 characters or fewer) do not trigger typo tolerance even in the Storefront API — this is expected. Use 4+ character queries to test typo tolerance.
Q3: Recent searches not appearing in the predictive empty state
Recent searches are stored in versioned localStorage via UisceShopperState. They are recorded when the shopper submits a search form (not on each keystroke).
Check:
window.UisceShopperStateexists (diagnostic snippet confirmsshopperState: true)recentSearchesCountin the diagnostic snippet — should be > 0 after submitting at least one search- Recent searches respect the 5-entry cap (oldest is pruned when a 6th is added)
- localStorage is available — if the shopper is in a private/incognito window or has localStorage blocked, recent searches are unavailable (feature degrades gracefully — block is hidden)
Q4: Popular searches block not appearing
Popular searches are rendered server-side from the Search results page section settings (popular_search_1..5). They appear only in the full search-page zero-results empty-state pack — the predictive drawer never renders them (its empty state carries recent searches and did-you-mean only).
Check:
- Are any
popular_search_*settings populated in the Search results page section in the theme editor? (Open the theme editor on the/searchtemplate to reach them.) - Run a search that returns zero results (e.g.
xyzzy123notaword) on the search results page — the popular-searches list renders in the empty-state pack. - If the block still doesn't show, verify a
[data-popular-searches]element inside the.search-empty-stateblock in DevTools → Elements on that zero-results page.
Q5: Typed tabs not appearing on search results page
Typed tabs (Products / Articles / Pages) only appear when the corresponding type has at least one result. If only products are found, only the Products tab appears.
Check:
- Does your store have published blog articles? Articles only appear in search results if at least one is published.
- Does your store have published pages? Same logic applies to the Pages tab.
- Try a generic search term like "about" or your store name — this often surfaces pages + articles.
- Verify
tabsCE: truein the diagnostic snippet — run it on the search results page. The registering script (search.js) loads only on search templates, sotabsCE: falseon other pages is by design;falseon the search results page itself means the script failed to load.
Q6: URL does not update when switching tabs
The <search-srp-tabs> Custom Element uses the History API (history.pushState) to update the URL with ?type=products, ?type=articles, or ?type=pages. This requires:
- JavaScript is enabled
- The browser supports
history.pushState(all modern browsers)
If the URL is not updating, open DevTools → Console → look for JavaScript errors. The CE should update the URL and the aria-selected state on tab buttons simultaneously.
Q7: Filters on the search SRP drop when switching tabs
This is by design. Filters are type-scoped — product filters (price, availability, variants) do not apply to articles or pages, so carrying them across tabs would be invalid. On tab switch the <search-srp-tabs> element rewrites the URL in place with history.pushState, keeping only the q and type parameters and stripping every other filter param. No navigation happens — the tab panels swap client-side.
Q8: "Viewport remount not supported" — what does this mean?
This is a known limitation documented in Phase 06.7: the predictive search drawer uses a one-shot loaded flag — once loaded, it does not reload on viewport changes (e.g., switching from mobile to desktop breakpoint). The drawer continues to function correctly after viewport changes; the limitation only affects the initial load state.
Decision tree: "Why is search not working?"
Predictive search shows nothing
Predictive search shows nothing
│
├─ Is JavaScript enabled?
│ No → search form still works (native /search endpoint)
│ Yes → continue
│
├─ Is the input focused with ≥ 2 characters?
│ No → by design (min 2 chars trigger)
│ Yes → continue
│
├─ Check console diagnostic: token: true?
│ false → Storefront token not set → follow token setup guide
│ true → continue
│
├─ Check console diagnostic: predictiveSearchBackend?
│ "rest" → no token configured (the flag is set server-side from the token setting; it never changes at runtime) → follow token setup guide
│ "graphql" → continue
│
├─ Network tab: POST to /api/2026-04/graphql.json returning 200?
│ 401/403 → token invalid or revoked → recreate token
│ 200 → response has results?
│ No results in response → store may have no matching products
│ Yes results → JS render bug → open a support issue with console outputTyped tabs not appearing
SRP shows only Products tab (or no tabs)
│
├─ Does the search query return articles?
│ No → publish blog articles in Shopify admin
│ Yes → continue
│
├─ Does the search query return pages?
│ No → create and publish a page in Shopify admin
│ Yes → continue
│
├─ Check diagnostic: tabsCE: true?
│ false → run the diagnostic ON the search results page (search.js loads only on search templates);
│ still false there → JS load failure → check browser console for errors
│ true → tab logic OK but results filtered?
│ data-active-tab on <search-srp-tabs> shows the server-rendered initial tab only (never updated
│ on click) — check aria-selected on the tab buttons for the live state in DevToolsConsole diagnostic snippet
Paste into browser DevTools Console on any storefront page. Read-only, adds zero theme code.
js
(() => {
const r = {
token: !!window.uisceShopConfig?.storefrontAccessToken,
tokenPrefix: window.uisceShopConfig?.storefrontAccessToken?.slice(0, 8) ?? null,
apiVersion: window.uisceShopConfig?.storefrontApiVersion,
predictiveSearchBackend: window.uisceShopConfig?.predictiveSearchBackend ?? 'not-set',
predictiveCE: !!customElements.get('predictive-search'),
tabsCE: !!customElements.get('search-srp-tabs'),
shopperState: !!window.UisceShopperState,
recentSearchesCount: window.UisceShopperState?.all('recent-searches')?.length ?? null,
};
console.table(r);
})();What to look for:
| Field | Expected value | What it means if wrong |
|---|---|---|
token | true | false → token not set; typo tolerance unavailable |
tokenPrefix | First 8 chars of your token | null → token not set; verify Theme Settings |
apiVersion | 2026-04 or later | Older version → may have different API behavior |
predictiveSearchBackend | "graphql" | "rest" → no token configured (the flag never changes at runtime) |
predictiveCE | true after focusing the search input | false before first focus/input is normal (the script lazy-loads); false after interacting → JS load failure |
tabsCE | true on the search results page | false outside search templates is by design (search.js loads only there); false on the search page → JS load failure |
shopperState | true | false → shopper-state.js not loaded; recent searches unavailable |
recentSearchesCount | Integer ≥ 0 | null → UisceShopperState absent (shopper-state.js not loaded); blocked localStorage reports 0, not null |
To confirm which backend is active, check window.uisceShopConfig.predictiveSearchBackend. This is set server-side by Liquid: 'graphql' when the Storefront Access Token setting is non-blank, 'rest' when it is blank. No validity check is performed — the JavaScript only reads the flag and never changes it.
Curl cheat-sheet
Replace {store} with your storefront domain (e.g., uisce.sionnach.solutions) and {token} with your Storefront Access Token.
Capture auth cookie (dev stores only)
Dev stores are password-protected. Capture the _shopify_essential cookie first:
bash
# Get CSRF token
curl -s -c cookies.txt https://{store}/password -o /tmp/pw.html
CSRF=$(grep -o 'name="authenticity_token" value="[^"]*"' /tmp/pw.html | head -1 | sed 's/.*value="//;s/"//')
# Submit password
curl -s -c cookies.txt -b cookies.txt \
--data-urlencode "form_type=storefront_password" \
--data-urlencode "authenticity_token=${CSRF}" \
--data-urlencode "password={preset-password}" \
-L "https://{store}/password" -o /dev/null
# Now add -b cookies.txt to all subsequent curl commandsTest Storefront API predictiveSearch query
bash
curl -X POST \
-H "Content-Type: application/json" \
-H "X-Shopify-Storefront-Access-Token: {token}" \
-d '{"query":"{ predictiveSearch(query:\"shrt\", limit:4, types:[PRODUCT]) { products { title } queries { text } } }"}' \
https://{store-myshopify-domain}/api/2026-04/graphql.json | jq .Expected: data.predictiveSearch.products array with products matching "shirt" (typo-corrected). Empty array → no products, or token lacks storefront access.
Test REST fallback (no token)
bash
curl -s "https://{store}/search/suggest.json?q=shirt&resources[type]=product&resources[limit]=4" | jq .Expected: resources.results.products array. Works without a token; no typo tolerance.
Deprecation note on /search/suggest.json
/search/suggest.json continues to work in Shopify 2026-04 but is the legacy endpoint. It returns products, collections, articles, pages, and query suggestions — the theme's REST fallback requests and renders all five resource types from it — but it does NOT support typo tolerance. Use the Storefront API predictiveSearch GraphQL query for typo tolerance.
Verify search SRP empty state (suggested collections)
bash
curl -s -b cookies.txt \
"https://{store}/search?q=xyzzy123notaword&type=product" | \
grep -c "suggested-collections\|empty-state"Expected: non-zero count confirming the empty-state block renders.
Google Rich Results Test URL template
For testing article structured data on a production store (no password protection):
https://search.google.com/test/rich-results?url={article_url_encoded}For password-protected dev stores, use HTML-paste mode:
bash
curl -s -b cookies.txt https://{store}/blogs/news/{article-handle} > /tmp/article.html
# Then paste /tmp/article.html content into:
# https://search.google.com/test/rich-results (click "Code" tab)Or validate the JSON-LD directly:
bash
curl -s -b cookies.txt https://{store}/blogs/news/{article-handle} | \
python3 -c "
import re, json, sys
html = sys.stdin.read()
blocks = re.findall(r'<script[^>]*type=[\"'\''']application/ld\+json[\"'\'''][^>]*>(.*?)</script>', html, re.DOTALL)
for b in blocks:
if 'BlogPosting' in b:
try:
d = json.loads(b.strip())
print('@type:', d.get('@type'))
print('mainEntityOfPage:', bool(d.get('mainEntityOfPage')))
print('articleSection:', bool(d.get('articleSection')))
print('inLanguage:', bool(d.get('inLanguage')))
print('wordCount:', bool(d.get('wordCount')))
except Exception as e:
print('JSON error:', e)
"Need more help?
- File a GitHub issue with the console-snippet output + browser/OS + preset name.
- See the Search guide for settings documentation.
Related docs
- Search guide — configuration walkthrough
- Article — audio, author bio, social share
- Product page support
- Collection page support
- Support form — submit a ticket if the above does not resolve your issue