L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Design et création · SKILL.md

woocommerce-faceted-navigation

woocommerce-faceted-navigation

Use when building, reviewing, or fixing faceted/layered navigation and product filters on a WooCommerce store — filter UX (AJAX vs reload, mobile filter drawer, applied-filter chips, sort logic), facet indexability strategy (index-worthy facet landing pages vs thin parameter combinations, canonical/noindex/robots decision matrix), crawl-budget and origin-lo…

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: woocommerce-faceted-navigation description: Use when building, reviewing, or fixing faceted/layered navigation and product filters on a WooCommerce store — filter UX (AJAX vs reload, mobile filter drawer, applied-filter chips, sort logic), facet indexability strategy (index-worthy facet landing pages vs thin parameter combinations, canonical/noindex/robots decision matrix), crawl-budget and origin-load protection (parameter normalization, cache-key design, allowlisted exposure), and Woo query/DB performance for high-cardinality attributes (attribute lookup tables, external index offload boundary). Trigger on - product filters, layered nav, faceted navigation, filter SEO, crawl trap, filter URLs indexed, AJAX filters slow. Category-page content checklists live in ecommerce-growth; generic schema and site architecture live in seo-structure-architect; Woo code security patterns live in woocommerce-developer. disambiguation: The FILTER and faceted-navigation layer only — filter UX, facet indexability, crawl control, and the SEO consequences of filtered URLs. Whole-page content and store-wide growth levers are ecommerce-growth.

WooCommerce Faceted Navigation

Overview

Faceted (layered) navigation lets shoppers narrow a catalogue by attribute — price, brand, colour, size, spec. Done well it is the single biggest discovery win on a large store; done badly it is a crawl trap, a duplicate-content generator, and an origin-load DDoS you inflicted on yourself. This skill is the HOW + crawl-control layer.

NeedGo to
WHAT to put on a category page (facet list, counts, sort, drawer)ecommerce-growth §Category Pages — the checklist; reference it, don't duplicate
Woo PHP code, security, HPOS query rules, external-search offloadwoocommerce-developer
Schema, site architecture (3-click), sitemaps, CWVseo-structure-architect

Four independent controls — never conflate them: UX state (what the shopper sees), crawlability (can a bot fetch the URL), indexability (may it appear in search), and cacheability (can the response be served from cache). A URL can be cacheable yet non-indexable, or crawlable yet noindexed. Design each deliberately.

Facet Taxonomy & Filter UX

  • Choose facets by demand, not by "every attribute we store." Expose the handful shoppers actually filter on; bury the long tail.
  • Show option counts and prevent zero-result states — disable or hide options that would return nothing for the current selection (see ecommerce-growth). A directly-requested invalid, duplicate, or zero-result filter URL should return 404, not a 200 empty page (per Google's faceted-navigation guidance) — that lets crawlers drop it.
  • AJAX vs full reload:
AJAX filteringFull page reload
UXFast, no scroll resetSlower, scroll resets
SEOFiltered state often invisible to crawlers unless URLs updateServer-rendered, crawlable by default
CacheAJAX endpoint responses need their own caching strategyUses normal page cache
Best forInteractive refinement on already-landed usersStates you WANT indexed as landing pages

Pattern that gets both: AJAX-update the results and push a clean, normalized URL via the History API so the state is shareable and (if allowlisted) crawlable.

  • Mobile: filters in a slide-in drawer with an "Apply" action, not inline; show the applied-count on the trigger.
  • Applied-filter chips above results (each removable) so shoppers can see and undo selections; include a "clear all."
  • Sort (best-selling / price / newest / rating) is a display control — keep sort in a parameter you can strip from the canonical, never a separately indexable URL.
  • Pagination: prefer numbered, crawlable pages over infinite scroll for indexable listings; "load more" is fine for UX but ensure the paginated set is still reachable via <a href> links for crawlers. Self-referencing canonicals per page (do NOT canonical page 2+ to page 1 — that hides deeper products).

URL & State Strategy

  • Query parameters (?brand=acme&color=black) are the pragmatic default in WooCommerce; pretty paths (/shop/acme/black/) look cleaner but are a double-edged sword (see caveat 5 below).
  • Normalize aggressively so one logical state has exactly one URL:
    • Fix parameter order (alphabetical) and value case; collapse duplicates.
    • Drop empty/default params; strip tracking params from the canonical.
    • Decide a single delimiter for multi-select and stick to it.
  • Preserve history/back-button behaviour via pushState/replaceState; make filter states shareable (the URL fully reconstructs the view).

Indexability Allowlist (core)

Treat facet URLs as allowlist, not blocklist: index a small, curated set of demand-backed combinations as landing pages; everything else is thin and should stay out of the index.

  • Index-worthy — single facets or proven double-facet combos with real search demand and unique, useful content (e.g. /gaming-pcs/rtx-5070/). Give them a self-referencing canonical, a tailored title/H1, and intro copy.
  • Thin / infinite — 3+ stacked facets, sort orders, price sliders, near-empty combinations. Keep these out of the index.

Decision matrix:

CombinationInternal linksCanonicalMeta robotsrobots.txt
Curated landing facetYes (crawlable <a>)Selfindex,followAllow
Non-curated but low-volumeNo(n/a)noindex,followAllow (so the noindex is seen)
Combinatorial explosion / sort / session paramsNo——Disallow the parameter pattern

NC4 caveats — non-negotiable, because each is a common way teams break their own site:

  1. noindex requires the page to be crawlable to be seen — Googlebot only honours a noindex if it can fetch the page and read the tag/header. A URL you both Disallow in robots.txt and mark noindex will keep the noindex unread.
  2. robots.txt disallow hides the noindex tag — the two directives conflict. Disallowed URLs can still be indexed (URL-only, from links) precisely because the crawler never sees the noindex. Pick one mechanism per URL.
  3. canonical is only a hint — rel=canonical is a suggestion Google may ignore; it consolidates signals, it does not guarantee the alternate is dropped, and it never stops crawling or serving of the variant.
  4. none of noindex/canonical/robots alone prevents origin load — noindex and canonical still get crawled (they must be, to be read); robots.txt stops Googlebot fetching but not users, other bots, or your own AJAX calls. Controlling crawl and cost is a separate job from controlling indexing (next section).
  5. naive pretty-URL rewrites can make facets MORE crawlable — rewriting ?brand=x&color=y into /brand-x/color-y/ turns parameter noise into clean, linkable, crawlable paths, multiplying the crawl surface. Pretty URLs are a commitment to index; use them only for the allowlisted set, never as a blanket rewrite.

Crawl-Budget & Origin-Load Protection

Indexability keeps junk out of search; this keeps junk off your server. Co-design them — cacheability is not indexability.

  • GET-parameter normalization (from URL & State): fewer distinct URLs = fewer crawl paths and higher cache hit-rate. Canonicalize order/case at the edge or in the app.
  • Cache-key design: decide which parameters vary the cache. Ignore/normalize params that don't change output so ?color=black and ?color=black&utm=x hit one cached object — but only ignore params proven inert; dropping a real facet, currency, or customer-segment param serves wrong content or enables cache poisoning. Cloudflare "Cache Rules" + "Query String Sort" (normalize param order), Varnish's default hash on full req.url (normalize in VCL), and LiteSpeed's per-query-string copies ("Drop Query String" only for inert params) all need explicit configuration — the defaults over- or under-cache facets. Verify real HIT/MISS/BYPASS headers.
  • AJAX response caching: filter AJAX endpoints bypass the page cache by default. Cache their JSON responses (object cache / transient / edge) keyed on the normalized facet set, or they become an uncached origin hit per interaction.
  • Internal-link discipline: only allowlisted combinations get crawlable <a href> links. Render non-indexable filters as buttons/JS controls (not anchors) so you are not advertising crawl paths you then try to suppress.
  • Monitor: watch GSC Crawl Stats and server logs for crawler time spent on parameter URLs; a spike there is the early signal of a crawl trap.

Woo Query & DB Performance

High-cardinality attribute filtering is where WooCommerce falls over at the database.

  • meta_query is the trap. Filtering on product attributes stored as post meta forces expensive wp_postmeta self-joins that scale badly with catalogue size and attribute count.
  • Use the attribute lookup table. WooCommerce maintains wc_product_attributes_lookup — a flat, indexed table purpose-built for fast attribute filtering. (Version history, since it is widely misremembered: WC 3.6 introduced wc_product_meta_lookup; the attribute lookup table wc_product_attributes_lookup arrived experimentally in WC 5.6 and became the default in WC 6.3. It is standard in WC 9.x/10.x.) Keep it regenerated (WooCommerce → Status → Tools) after bulk imports.
  • Cache expensive aggregations (facet counts, price ranges) in transients / a persistent object cache (Redis/Memcached); recompute on product save, not per request.
  • When to offload: past a few thousand products, or with many high-cardinality attributes and free-text search, move filtering/search to an external index — ElasticPress (Elasticsearch/OpenSearch) or Algolia. Keep WooCommerce as the source of truth and treat the index as a rebuildable projection. The mechanics of that offload (sync, keys, security) live in woocommerce-developer §Performance → Search Offload — do not duplicate them here.

Build vs Plugin

OptionWhen it fitsWatch for
FacetWP-class pluginYou need fast time-to-value and it exposes the controls belowVerify it lets you control indexability + cache behaviour
Custom buildYou need exact crawl-control / cache-key / index semanticsMore engineering; you own the query strategy

Evaluate any filter plugin against: (1) does it emit clean, normalizable URLs; (2) can you set canonical/noindex per facet state; (3) does it use the attribute lookup table (not raw meta_query); (4) how do its AJAX endpoints interact with your page cache and CDN; (5) does it let you withhold internal links from non-allowlisted combos.

Anti-Patterns

Don'tWhy
Expose every filter combination as a crawlable <a href>Crawl trap + thin duplicates; only allowlisted combos get links
Disallow a URL in robots.txt AND rely on its noindexThe crawler never sees the noindex — the page can still get indexed
Blanket-rewrite ?params into pretty pathsMultiplies the crawlable surface (caveat 5)
Canonical page 2+ back to page 1Hides deeper products from indexing
Trust canonical/noindex to cut server loadThey must be crawled to work; crawl/cost control is separate
Filter high-cardinality attributes via raw meta_querywp_postmeta self-joins melt at scale — use wc_product_attributes_lookup
Leave filter AJAX endpoints uncachedOne uncached origin hit per interaction
Return a 200 empty page for an invalid/zero-result comboReturn 404 so crawlers drop it (Google faceted-nav guidance)
Duplicate the search-offload recipe hereIt lives in woocommerce-developer — reference it
Skills enregistrées