
Photo by Marvin Meyer on Unsplash
Astro Ecommerce Templates: Islands Architecture for Content-Heavy Stores
Astro Ecommerce Templates: Islands Architecture for Content-Heavy Stores
Astro isn't the right framework for every ecommerce store — but when it fits, it wins by a wide margin. Astro's islands architecture ships zero JavaScript by default and only hydrates components explicitly marked as interactive. For stores where most pages are browsing, editorial content, or catalog lookups rather than heavy client-side state, this produces the best Core Web Vitals in the MVPHub catalog, beating even SvelteKit on content pages.
TL;DR
- Astro's default page has zero JavaScript — it's pure HTML + CSS, rendered at build time (SSG) or request time (SSR)
- Interactive "islands" are React/Vue/Svelte components you mark for hydration with
client:load,client:idle,client:visible, orclient:media - Hybrid rendering — SSG for content pages, SSR for product pages with live inventory, chosen per-route
- Best Core Web Vitals in the catalog, by a wide margin — 0.8s mobile LCP and a perfect 100 Lighthouse score on the measured product page (2.5× faster than any other web framework in the catalog)
- Framework-agnostic — use React, Vue, Svelte, or Solid components in the same project
- Pick Astro when your store is catalog-browsing heavy, editorial-heavy, or SEO-dominant revenue channel
Browse Astro ecommerce templates
Why islands architecture wins
Every other web framework ships framework runtime JavaScript to the browser — React, Vue, Svelte (slightly), Angular, Solid. Even if your page is mostly static, you pay the framework tax on every load.
Astro inverts this. The default Astro page is static HTML with zero JavaScript. Your components are Astro components (.astro files) that render server-side and ship as HTML. The client pays exactly zero framework cost for static content.
Where Astro gets interesting is the island. An island is a component from React, Vue, Svelte, or Solid that you embed in an Astro page and mark as interactive:
---
// product-detail.astro
import { CartDrawer } from '~/components/CartDrawer.tsx'; // React island
import { VariantPicker } from '~/components/VariantPicker.svelte'; // Svelte island
import ProductGallery from '~/components/ProductGallery.astro'; // static Astro
import { medusa } from '~/lib/medusa';
const { handle } = Astro.params;
const product = await medusa.products.retrieve(handle);
---
<article>
<h1>{product.title}</h1>
<!-- Static Astro component — zero JS -->
<ProductGallery images={product.images} />
<!-- Svelte island — hydrated on page load -->
<VariantPicker client:load variants={product.variants} />
<!-- React island — hydrated when it scrolls into view -->
<CartDrawer client:visible />
</article>
Each island hydrates independently with its own minimal runtime. The rest of the page stays as static HTML. If you don't use a particular island on a page, you don't pay for it.
The result: a typical Astro product page ships zero first-party JavaScript by default — interactive islands opt into hydration explicitly. The measured kids-store-astro benchmark variant is a 5 KB document that Lighthouse scores 100/100 on both mobile and desktop. This is the lowest overhead of any framework in the MVPHub catalog.
Hydration directives
Astro gives you granular control over when each island hydrates:
| Directive | Hydration trigger |
|---|---|
client:load | On page load (use for above-the-fold interactive UI) |
client:idle | After the main thread is idle (use for below-the-fold interactive UI) |
client:visible | When the component scrolls into view |
client:media="(max-width: 768px)" | Only if the media query matches (use for mobile-only UI) |
client:only="react" | Skip SSR entirely, hydrate client-side only |
The right directive for each island depends on its priority:
- Cart drawer →
client:visibleorclient:idle— not needed until a user interacts - Variant picker →
client:load— needs to be interactive immediately on product pages - Related products carousel →
client:visible— below the fold - Mobile menu toggle →
client:media="(max-width: 768px)"— desktop users don't need it - Live chat widget →
client:idle— never blocks initial render
Getting these right is the main learning curve for Astro. You're making explicit decisions about when to pay the hydration cost. In Next.js you don't make these decisions — everything 'use client' hydrates on load.
Hybrid rendering: SSG + SSR per route
Astro 5 supports three rendering modes that can coexist in one project:
SSG (default) — pre-rendered at build time. Use for:
- Homepage
- Blog posts
- About, FAQ, shipping, returns pages
- Category landing pages (if catalog is stable)
SSR (export const prerender = false) — rendered per request. Use for:
- Product detail pages with live inventory
- Cart page
- Checkout flow
- Account pages
Static with ISR-like behavior — pre-rendered at build time with on-demand revalidation via your hosting platform (Vercel, Cloudflare).
A typical Astro ecommerce config:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel/serverless';
export default defineConfig({
output: 'hybrid',
adapter: vercel(),
// Most pages are static by default
});
---
// src/pages/products/[handle].astro
export const prerender = false; // Opt into SSR for this route
---
This is cleaner than Next.js's server-component-by-default with 'use client' opt-outs. Astro makes you declare rendering intent at the top of each route.
Architecture: Astro + Medusa
MVPHub Astro ecommerce templates ship with Medusa v2.
src/
pages/
index.astro # Homepage (SSG)
products/
[handle].astro # Product detail (SSR)
collections/
[handle].astro # Collection browse (SSR)
cart.astro # Cart page (SSR)
checkout.astro # Checkout (SSR)
blog/
[slug].astro # Blog post (SSG)
components/
Astro/ # Static components
ProductGallery.astro
SiteHeader.astro
Svelte/ # Interactive islands
VariantPicker.svelte
CartDrawer.svelte
AddToCartButton.svelte
lib/
medusa.ts
cart.ts
Static components are pure Astro (.astro files) — zero JS. Interactive components are Svelte islands because Svelte has the smallest hydration cost of any framework you can embed in Astro. You could use React instead for bigger ecosystem, or Vue if that's your team's language — Astro doesn't care.
Performance benchmarks
Mobile product page, Slow 4G throttling:
| Metric | Astro | SvelteKit | Next.js |
|---|---|---|---|
| LCP | 0.8s | 2.1s | 2.0s |
| INP | 95ms | 85ms | 120ms |
| CLS | 0.00 | 0.00 | 0.00 |
| First-party JS (content page) | 0 KB | ~40 KB | ~150 KB |
| First-party JS (product page) | 0 KB (zero islands variant) | ~40 KB | ~150 KB |
| Lighthouse score | 100 | 98 | 99 |
Astro wins decisively on both content AND product pages, beating SvelteKit by ~1.3s mobile LCP and Next.js by ~1.2s. Its observed (unthrottled) paint time is 174ms end-to-end — FCP, LCP, and DomContentLoaded all fire at the same frame. SvelteKit and Next.js are essentially tied. See /benchmarks for the full dataset.
When to pick Astro
Good fit:
- Content-heavy catalogs (editorial product pages, lookbooks, buying guides)
- Stores with long-form content marketing strategies
- Brands where SEO is the dominant revenue channel — Astro's CWV advantage is measurable
- Teams willing to think about hydration boundaries explicitly
Not a fit:
- Highly interactive stores where every page has complex client-side state
- Teams uncomfortable with hydration directive decisions
- Stores that need deep integration with React-specific ecommerce libraries (though you CAN use React islands, the Astro-first patterns don't always match what the library expects)
Common questions
How does Astro handle cart state across pages?
Cart state typically lives in one of two places:
-
Server-side cart with cookies. The cart ID lives in an HTTP-only cookie. Each page fetches the cart in its frontmatter (server-side) and passes line items to client islands. The cart drawer is an island that reads from a URL-based or localStorage cache for instant UX.
-
Client-side Nanostores. Astro ships
nanostores— a tiny state library (~1 KB) that works across islands. The cart drawer and "add to cart" button share state via a Nanostore that persists to localStorage.
Both patterns are supported in MVPHub Astro templates.
Can I use Tailwind CSS with Astro?
Yes. Astro has first-class Tailwind support — run npx astro add tailwind and it's configured. Every MVPHub Astro template uses Tailwind.
Which framework should I use for islands?
For commerce use cases:
- Svelte — smallest runtime, best performance, cleanest syntax for small interactive components
- React — biggest ecosystem, easiest to find pre-built components, familiar to most teams
- Vue — middle ground, smaller than React runtime, larger than Svelte
- Solid — fast and small, niche adoption
MVPHub Astro templates default to Svelte islands because the performance story is most consistent with Astro's zero-JS philosophy.
Is Astro good for authenticated experiences (account, orders)?
Yes, but the authentication pattern is different from Next.js. Astro uses middleware for auth checks — run a middleware.ts at the project root that validates session cookies and either passes through or redirects. Per-route auth lives in the .astro frontmatter.
Does Astro work with Shopify Storefront API?
Yes. Like every framework in the catalog, Astro's data layer is just fetch() calls. Swap Medusa for Shopify by replacing the client library. MVPHub Astro templates ship Medusa by default.
Next steps
- Browse Astro ecommerce templates
- Framework comparison guide
- SvelteKit templates — another low-JS alternative
- Template Finder quiz — guided recommendation
- Performance benchmarks — real Lighthouse data
- Compatibility matrix
If your store is catalog-browsing-heavy and SEO dominates your revenue channel, Astro is the strictly best choice in the catalog. The zero-JS default produces Core Web Vitals no other framework can match on content pages, and the islands architecture lets you add interactivity exactly where you need it without paying for it elsewhere. Pick it when your store's shape matches the philosophy.







