A Liquid Partials Experiment

I’ve been treating Liquid partials as a small experiment: can a Liquid theme feel like an SPA without going headless?

Short answer: closer than I expected.
Longer answer: the fun part works; the platform glue still needs shopify engineering

The experiment

I forked Horizon 4.1.3 and wired up the partial-rendering runtime so the header, footer, and cart chrome stay in the DOM. Only a page-content region swaps when buyers navigate - collection to product, header links, that kind of browse flow. History API, prefetch on hover, View Transitions, the usual polish.

It maps cleanly onto how people already think about SPAs:

Persistent shell - nav, cart, drawer state survive route changes

Server-rendered HTML - Liquid stays the source of truth; no parallel GraphQL UI layer

Named regions - fetch and replace what changed, not the whole document

In my benchmarks, collection to product went from about 1.2 seconds (full reload) to about 49 ms (partial swap).

Live demo (password: ocontis):

The header does not blink when you click a product card.

Standard storefront events shopify:page:view, etc. work after each swap. Web pixel customer events page_viewed, etc. do not - WPM init is once-per-document, and the only fix I found was a theme-side intercept before content_for_header. It works; it should not live in theme code.

I think partials are a good fit for SPA-like architecture inside the Online Store stack and there might be a nice opportunity there. Shopify stepping in on pixel lifecycle (and maybe shell UX patterns) would keep it from feeling hacky.

Try it on github.
Full technical and performance breakdown on my blog

Curious if anyone else is experimenting with partials for in-store navigation.

This is very cool! I’ll see what we can do about web pixels on our side.

@Gray-Shopify oh that would be so nice! at the moment that was the only “production ready” blocker i could find to be fair. Now of course a lot of apps inject stuff in content_for_header which is another story. However, i an ideal world where event lifecycle is managed via shopify pixels, anything that foes into content_for_header should in theory only need to load once.

On a similar note, I’ve been quite surprised to find out that pageview events on theme and pixels are managed completely differently. We live and learn!