Skip to content

Storefront data, what's available where

Per-page data availability, the extra.* contract, and cart/checkout/account integration.

Updated View as Markdown

Templates read data from two sources: the context globals (the StorefrontContext, see Globals) and the extra.* bag, which the route handler stuffs with page-specific data. This guide is the practical map of what’s available on each page type, following the default theme’s usage.

Context globals by page type

Page key Always available Present on this page
index shop, settings, collections, linklists, request, extra , (sections carry their own data via extra)
product same product (full: variants, metafields, tags), cart (if a cart token is present)
collection same collection; listing data in extra.listing
catalog same listing data in extra.listing
search same search.query / search.results_count; results in extra.listing
cart same cart (server-rendered page)
checkout same order_id (confirmation branch)
page (CMS) same page (handle, title, content_html)
blog / article same blog, article
account/* same session-driven; client-rendered via data-*

Missing globals render empty, never crash (product.name on an index page → "").

The extra.* contract

extra is a catch-all map the route handler fills per request. The default theme depends on these keys, if you build a theme, negotiate this contract with the API:

Key Shape Consumed by
extra.listing { items, total, filters?, … } catalog_products, collection_products, search_results, collection_filters
extra.listing.filters [{ groups: [{ options: [{value,label,active,count}] }] }] collection_filters
extra.sort string listing_toolbar
extra.active_collection { slug } catalog_products
extra.collection { name, description, slug } collection_products
extra.collections [{ slug, name, image_url }] collections_list
extra.featured_products product array featured_products, product_recommendations (fallback)
extra.recommendations product array product_recommendations
extra.query string search_results (search bar value)
extra.order_id / extra.order , checkout, account/order
extra.register_message string account/register
extra.reset_token string account/reset_password

Products in extra arrays are plain dicts with slug, name, featured_image / image_src, base_price / price_range_min / price, compare_at_price, currency_code, on_sale, consumed by the product_card macro. They are not ProductObjects (no .variants, no .metafields), because they come from the listing queries, not the product detail query.

The product object on PDP

On product pages, product is a full ProductObject:

  • Scalars: name, slug, description, base_price, compare_at_price, currency_code, available, featured_image, tags, category_id.
  • product.variants, array of { id, title, price, available, metafields }; variant_picker and buy_buttons branch on product.variants|length > 1.
  • product.metafields, the lazy map: product.metafields.custom.rating (see Data layer).

The default theme’s PDP (product_detail.jinja) is the reference consumer: it renders gallery + info columns by dispatching over children, and references product.* for prices (| money), compare-at + discount chips, vendor, share links (request.path | urlencode), and metafield blocks.

Cart and forms

The theme wires cart behavior through main.js with data-* hooks, not through bespoke endpoint knowledge:

Hook Purpose
data-cart-badge cart count in navbar
data-cart-toggle / data-cart-backdrop / data-cart-close drawer open/close
data-add-to-cart + data-variants PDP add-to-cart (fetch, serialized)
data-quick-add product-card quick add
data-qty-stepper + data-action quantity steppers
data-rr-cart-remove / data-rr-cart-item cart page remove
data-rr-newsletter-form / data-rr-contact-form fake-submit forms
data-rr-auth-form account auth forms
data-rr-checkout-form + data-rr-checkout-* checkout form + client summary

The cart is mirrored client-side in localStorage (ringroad:cart) and synced with the server-rendered cart page. buy_buttons posts directly to /api/v1/shop/{shop.domain}/cart/items server-side, the one endpoint the default theme knows about.

Request metadata

request.path and request.page_type enable conditional rendering (share links, active nav states). shop.domain builds store-scoped URLs.

The smoke test contract

tests/default_theme_smoke.rs renders the real default theme with stub data, if you change what your templates read from globals or extra, that test (or the fixtures feeding it) is where the renderer’s view of your contract is pinned.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close