Storefront Structure
This guide uses Shoppr to show how Coil expects a storefront to be structured in a real customer app.
The key idea is simple: the storefront is a set of customer-owned pages and templates that use module data, not one platform-owned catalog widget.
Start With The Route Shape
The right mental model is a decision journey, not a bag of pages:
home -> catalog -> collection -> product -> cart
Each step has a different job:
- home
- campaign entry and brand framing
- catalog
- broad browse and discovery
- collection
- merchandising context
- product
- buying decision
- cart
- continuity into checkout
Coil works best when the storefront is explicit at this level instead of hiding the whole browse loop behind one generic listing view.
Where The Routes Come From
The route contracts come from the commerce module manifest in
crates/coil-commerce/src/module/platform/manifest.rs.
That manifest defines routes such as:
/shop/shop/collections/shop/collections/{collection_slug}/shop/products/{product_slug}/cart
Shoppr then provides the customer-owned templates that make those routes feel like Shoppr instead of a generic store.
Layouts And Shared Shell
The storefront shell should separate document chrome from route-specific markup.
A good pattern looks like this:
<html xmlns:coil="https://coil.rs" coil:attr="lang=${locale}">
<body>
<coil:include src="navigation/primary.html" />
<main>
<coil:slot />
</main>
</body>
</html>
Then individual route templates fill the slot.
That keeps:
- document shell in one place
- navigation reusable
- promotional fragments reusable
- route templates focused on route work
Home Page Structure
apps/shoppr/templates/pages/home.html is worth studying because it combines several concerns that
real stores need:
- campaign framing
- links into catalog and collections
- market and locale visibility
- links to cart, account, admin, and CMS surfaces
- reused catalog fragments instead of duplicated markup
If you are designing your own storefront, treat the home page as a product entry surface, not just a marketing hero.
Product Detail As The Critical Page
apps/shoppr/templates/commerce/product-detail.html is the most important page in the demo.
It keeps together:
- breadcrumbs and collection context
- media gallery markup
- size and quantity controls
- add-to-cart and buy-now actions
- supporting product facts and details
- related product browsing
This is a good Coil page to copy structurally because it stays HTML-first even when the theme adds richer behaviour.
How The Product Template Gets Its Model
This is the part that often feels disconnected if you only read the template.
In Shoppr, the product-detail page is not a free-floating HTML file. The binding path is:
- the commerce module contributes route
commerce.product-detail - that route is wired to template
commerce/product-detail - the runtime adds the shared request keys and then appends
product,product_cards,has_product, and related fields for that route apps/shoppr/templates/commerce/product-detail.htmlconsumes those fields directly
The important consequence is that this markup:
<h1 coil:text="${product.name}">Harbor Cap</h1>
<p class="product-page__price" coil:text="${product.price}">GBP 29</p>
<p coil:text="${product.summary}">Product summary</p>
only works because the runtime has already shaped a product object for commerce.product-detail.
That object is not being invented inside the template. It is supplied by the runtime render-model binding for that route.
Progressive Enhancement Layer
The interaction layer lives in:
apps/shoppr/theme/assets/site.jsapps/shoppr/theme/assets/site.css
On the storefront, that layer currently owns:
- home carousel behaviour
- market and locale switcher panels
- PDP gallery thumbnails
- PDP accordions
- size selection state
- visible focus styling and reduced-motion behaviour
That is the pattern to copy:
- make the markup correct first
- add JavaScript as a progressive layer
- keep the route and form structure server-rendered
Full Implementation
If you want the complete Shoppr storefront after learning the structure:
apps/shoppr/templates/pages/home.htmlapps/shoppr/templates/commerce/catalog.htmlapps/shoppr/templates/commerce/collection-detail.htmlapps/shoppr/templates/commerce/product-detail.htmlapps/shoppr/templates/commerce/cart.htmlapps/shoppr/templates/layouts/base.htmlapps/shoppr/templates/layouts/storefront.htmlapps/shoppr/templates/navigation/primary.htmlapps/shoppr/templates/components/hero.htmlapps/shoppr/theme/assets/site.js
The route and model side of the same page lives here:
crates/coil-commerce/src/module/platform/manifest.rscrates/coil-runtime/src/render/model.rs
What To Copy Into Your Own App
If you are building a Coil storefront, copy this structure before you copy Shoppr's exact look:
- top-level page per customer decision stage
- layouts and fragments for repeated shell markup
- HTML-first forms for cart and checkout flow
- small theme JS for enrichment, not ownership of the product model
Common Mistakes
- Putting all storefront logic into one oversized home or catalog page.
- Treating the customer app as a template override layer instead of the real storefront owner.
- Letting JavaScript own critical product behaviour that the server-rendered page should still express.