Skip to main content

How Facets Work

Facet runtime is target-centric. Every control identifies a target, and every Facet Target with the same target ID participates in that target scope.

Render and Refresh Lifecycle

  1. During the original page render, FacetBlockInterceptor detects Facet Target components.
  2. The target's loop and fallback slots are normalized and stored as the refresh template.
  3. Facet controls register query descriptors for the same target ID.
  4. On interaction, the client builds scoped facet clauses and calls the facet REST endpoint.
  5. The endpoint applies those clauses to the target loop config, re-renders the stored target template, computes pagination and availability metadata, and returns markup.
  6. The client replaces the target content, updates URL state, updates option availability, applies pagination metadata, and reruns returned scripts when the target allows it.

Target ID Rules

  • The target string is the connection between controls and result containers.
  • Controls with different target IDs do not affect each other.
  • Multiple target containers can share a target ID, but they share the same scoped request state.
  • Empty target IDs are not useful at runtime; the PHP components show inline builder guidance when target is missing.

Facet Target Behavior

Facet Target owns the content that gets replaced. Its loop slot is the normal result state, and its fallback slot is shown when the refreshed loop produces no rendered items.

Important target props:

PropMeaning
targetShared target ID.
rerun_scriptsAllows scripts returned by refreshed markup to run again. Defaults to true.
preview_fallbackShows the fallback slot in Etch preview instead of the loop slot.
scroll_to_topScrolls to the target after connected facet updates.
used_with_mapMarks the target as map-paired, so map and result updates coordinate correctly.
load_more_on_scrollAdds internal offset and posts-per-page controls for infinite scroll.
load_more_batch_sizeBatch size used by infinite scroll.

Nested loops have a specific rule: if a Facet Target lives inside another Etch loop, outer loop params must be resolved during the original render pass. The current implementation freezes those params through FacetLoopParamResolver before the target template is stored, and the E2E nested_loop_context scenario verifies that refreshed results keep the correct outer-loop context.

URL State

Facet state is serialized under the target ID. The E2E URL tests cover direct-link hydration for search, checkbox, radio, select, pagination, map state, ACF relationship, and mapped bundle filters.

Examples:

ome[products][category_name]=shoes
ome[products][meta_value:price]=50
ome[products][tax_query:facet_color]=red
ome[products][acf_relationship:facet_related_posts]=123
ome[products][geo_bbox]=40.5,-74.3,40.9,-73.7

The exact URL key depends on the facet clause type and mode-specific options such as meta_key, taxonomy, or date column.

Pagination, Load More, and Reset

  • Pagination Facet writes pagination state for a target and receives total pages, total items, current page, per-page, and offset metadata from responses.
  • Load More Facet writes hidden offset and posts-per-page controls, then appends another batch through the same target request path.
  • load_more_on_scroll on Facet Target uses the same internal offset mechanism without requiring a visible button.
  • Reset Facet clears all controls connected to the target and returns the target to its unfiltered state.

When any normal facet value changes, pagination resets to page 1. The multi-facet E2E scenario verifies that changing category_name after navigating to page 2 resets pagination before showing the narrowed result set.

Map Pairing

Map Facet is a facet control with type geo_bbox. It sends the map viewport as a bounding box and reads Map POI elements from the connected target loop.

Use a map-paired setup when:

  • the target loop renders listings,
  • each listing includes a Map POI,
  • the map and result target share the same target ID,
  • the target sets used_with_map when map coordination is needed.

The map runtime can use detailed POI markup for smaller sets and lightweight coordinate responses for large sets.