Case study
Heal + Co.
Svelte islands inside Liquid, and why not React here
- Sector
- ecommerce
- Year
- 2025
- Role
- Frontend engineer
- Live
- healco.com
- runtime framework costSvelte compiles the runtime away
- ~0
- Shopify Liquid
- TypeScript
- Svelte
- PhotoSwipe
- IndexedDB
- webpack 5
01
The decision
A Shopify theme is already server-rendered and already fast at delivering HTML. The interactive parts — a filter, a gallery, a quantity selector, a drawer — are small, numerous, and independent. What they need is state and events. What they do not need is a framework runtime shipped ahead of them.
React's runtime is a fixed cost paid before the first island does anything, and on a theme with a dozen small islands across a dozen templates that cost is paid on every route whether or not the island is used. Svelte compiles to direct DOM operations with effectively no runtime, so the cost of an island is the island. For this shape of problem that is simply the right trade.
02
Where I do reach for React
The inverse case is a single large stateful surface — a multi-step configurator, an authenticated ordering flow, an admin app. There the runtime is amortised across a lot of behaviour, and the ecosystem access, the type maturity and the fact that every engineer on the team already reads it fluently all start to dominate.
The honest version of this is that both work, and the reason to have a rule is consistency rather than performance. Many small independent islands: Svelte. One large stateful application: React. The mistake is not picking wrong, it is picking differently on every project and leaving the next engineer to figure out which convention applies where.
- Many small, independent, mostly-presentational islands → Svelte
- One large stateful flow, or anything needing the wider ecosystem → React
- Either way: the surrounding page stays server-rendered Liquid
03
The build itself
Product galleries via PhotoSwipe with proper focus trapping and keyboard navigation, catalogue caching in IndexedDB for repeat visits on mobile, and the shared webpack pipeline that treats each section as its own entry point so a template only downloads the JavaScript it actually uses.