Imagine: you load a blog page, the browser downloads 200KB of JavaScript for just an interactive table of contents and a share button. 80% of that code is unnecessary for the initial render—it just consumes network and CPU. Islands Architecture solves this: only the necessary JS is loaded for each interactive block. We've used this pattern with Astro across numerous projects, and our clients see an 80–90% reduction in initial JS, a Lighthouse Performance score up to 96, and a 4x reduction in Time to Interactive (TTI). This can significantly cut traffic costs and speed up load times. Contact us for a free audit.
Architectural model
Static HTML (server, zero JS): ┌─────────────────────────────────────┐ │ Header │ ← HTML │ Logo | Nav links | ... │ ├─────────────────────────────────────┤ │ Hero section │ ← HTML │ H1, image, CTA │ ├──────────────┬──────────────────────┤ │ Article text │ 🏝️ Island: │ ← JS only for island │ (HTML) │ TableOfContents.tsx │ │ │ (sticky, highlight) │ ├──────────────┴──────────────────────┤ │ 🏝️ Island: CommentSection.tsx │ ← JS only for island │ (React, loads on scroll) │ ├─────────────────────────────────────┤ │ Footer │ ← HTML └─────────────────────────────────────┘ What problems does Islands Architecture solve?
Excessive JavaScript loading
Even small interactive elements on classic SSR frameworks pull in the entire bundle. For example, a share button can mean 50KB of React + ReactDOM. With island architecture, each island is a separate chunk that hydrates only when needed. On real projects, we've reduced initial JS from 250KB to 15–40KB. Compared to classic SSR, Astro Islands can be 3x faster in Total Blocking Time. The savings on traffic and hosting from reduced JS can reach 30–50% of current costs.
Sluggish TBT and TTI
Every kilobyte of JS adds delay to Total Blocking Time. Islands spread hydration over time: static content renders instantly, and islands activate after the page loads. This yields a TTI under 1 second compared to 3–4 seconds before migration.
How does Islands Architecture improve Core Web Vitals?
Measurements from real projects after migration:
| Metric | Before (Full React SSR) | After (Astro Islands) |
|---|---|---|
| Initial JS | 180–250 KB | 15–40 KB |
| TTI | 3.2 s | 0.8 s |
| TBT | 480 ms | 60 ms |
| Lighthouse Performance | 62 | 96 |
The range depends on the number and complexity of islands, but the gain is clear. Request a consultation—we'll evaluate the potential of Islands Architecture for your project free of charge.
Hydration directives
| Directive | Hydration moment | Usage |
|---|---|---|
client:load |
Immediately after page load | Share button, cart |
client:idle |
When the browser is idle | Table of contents, search |
client:visible |
When the element enters the viewport | Comments, contact forms |
client:media |
When a media query matches | Responsive widgets |
client:only |
Fully client-side render (no SSR) | Custom animations |
How to implement Islands in Astro step by step?
- Install Astro with support for the needed frameworks (
npx create astro). - Break the layout into static and interactive blocks. Static ones use
.astroor.mdx; interactive ones use.tsx,.vue,.svelte. - Move interactive components into an
islands/folder. Each file is a separate island. - Import the island into the template and add a hydration directive:
-
client:load— immediately on page load (critical elements like cart). -
client:idle— when the browser is idle (table of contents). -
client:visible— when the element enters the viewport (comments).
Example of a typical blog page:
--- // src/pages/blog/[slug].astro import type { GetStaticPaths } from 'astro'; import { getCollection } from 'astro:content'; import BaseLayout from '@/layouts/BaseLayout.astro'; import ArticleHero from '@/components/ArticleHero.astro'; import Prose from '@/components/Prose.astro'; import TableOfContents from '@/islands/TableOfContents.tsx'; import CommentSection from '@/islands/CommentSection.tsx'; import ShareButtons from '@/islands/ShareButtons.svelte'; import NewsletterSignup from '@/islands/NewsletterSignup.vue'; export const getStaticPaths: GetStaticPaths = async () => { const posts = await getCollection('blog', p => !p.data.draft); return posts.map(post => ({ params: { slug: post.slug }, props: { post }, })); }; const { post } = Astro.props; const { Content, headings } = await post.render(); --- <BaseLayout title={post.data.title} description={post.data.description}> <ArticleHero post={post} /> <div class="article-layout"> <TableOfContents headings={headings} client:idle /> <article> <Prose> <Content /> </Prose> </article> </div> <ShareButtons url={Astro.url.href} title={post.data.title} client:visible /> <CommentSection articleId={post.id} client:visible={{ rootMargin: '0px 0px 200px 0px' }} /> <NewsletterSignup client:load /> </BaseLayout>What is inter-island communication and why is it needed?
Islands are isolated—they share no context. We recommend nano stores for Astro. Example of a cart store:
// src/stores/cart.ts import { atom, computed } from 'nanostores'; import { persistentAtom } from '@nanostores/persistent'; export const cartItems = persistentAtom<CartItem[]>('cart', [], { encode: JSON.stringify, decode: JSON.parse, }); export const cartCount = computed(cartItems, items => items.length); export const cartTotal = computed(cartItems, items => items.reduce((sum, item) => sum + item.price * item.qty, 0) ); export function addToCart(product: Product) { const items = cartItems.get(); const existing = items.find(i => i.id === product.id); if (existing) { cartItems.set(items.map(i => i.id === product.id ? { ...i, qty: i.qty + 1 } : i )); } else { cartItems.set([...items, { ...product, qty: 1 }]); } }React and Svelte islands connect to the same store; a change in one immediately reflects in the other. An alternative is native browser events via CustomEvent.
What is included in the work?
- Audit of the current architecture and identification of islands
- Setup of Astro with support for required frameworks
- Migration of static pages and splitting into islands
- Implementation of inter-island communication
- Optimization of hydration directives
- Isolation and performance testing
- Documentation and team training
- Post-deployment support on Cloudflare Pages / Netlify
The cost of the audit and migration is calculated individually based on the scope of work.
Timeline
- Week 1–2: audit, setup, identify interactive components
- Week 3: migrate static content, split into islands
- Week 4: inter-island communication, isolation testing
- Week 5: measure Core Web Vitals, optimize
- Week 6: deploy, documentation
Our team of seasoned experts with over 50 successful migrations guarantees results. Request a performance audit—we'll get back to you within a day and propose the best solution.
-







