/blog/Apr 19, 2026

View Transitions API in a Shopify theme

How I wired up AJAX page transitions in a Dawn-based Shopify theme with about 30 lines of vanilla JS and zero build tools.

shopifyjavascriptcss

Shopify themes are a constrained environment: Liquid templates, no bundler, no framework, and a build system you don't control. That constraint makes the View Transitions API a good fit — it's a browser API that needs almost no JavaScript to produce a meaningful improvement.

What it does

document.startViewTransition() takes a callback that updates the DOM. The browser captures before/after states and animates between them. For a storefront that means navigation feels instant instead of causing a full white-flash reload.

The implementation

async function navigate(url) {
  const response = await fetch(url);
  const html = await response.text();
  const doc = new DOMParser().parseFromString(html, 'text/html');
 
  document.startViewTransition(() => {
    document.querySelector('main').innerHTML =
      doc.querySelector('main').innerHTML;
    window.history.pushState({}, '', url);
  });
}
 
document.addEventListener('click', (e) => {
  const link = e.target.closest('a[href]');
  if (!link) return;
  const url = new URL(link.href);
  if (url.origin !== location.origin) return;
  e.preventDefault();
  navigate(url.href);
});

Intercept internal link clicks, fetch the target page, swap <main>, push history. The transition handles the rest.

What to watch out for

Shopify sections that reinitialise on load. Any JS that runs in DOMContentLoaded won't re-run after a client-side swap. Move section init into a function you can call after the transition callback resolves.

Cart state. The mini-cart lives outside <main>. Fetch and update it separately, or you'll have a cart counter showing stale data after navigation.

prefers-reduced-motion. Wrap the transition in a check:

const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!prefersReduced && document.startViewTransition) {
  document.startViewTransition(swap);
} else {
  swap();
}

Browser support

Chromium only as of early 2026, with Firefox behind a flag. Good enough for a storefront where the majority of traffic is Chrome on Android — and the fallback (a normal navigation) is seamless because the code degrades cleanly.

Thirty lines of JS. No build step. Noticeably better than a full page reload.