Islands Architecture Implementation for Web Application

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1212
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    815

Implementing Islands Architecture for a web application

Islands Architecture — architectural pattern where a page consists of static HTML ocean with isolated "islands" of interactivity. Each island is independent component with its own JS, hydrating separately, unaware of neighboring islands.

Jason Miller (Preact) described concept in 2020. Today it's foundation of Astro, Marko, and part of Qwik approach.

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
└─────────────────────────────────────┘

Result: JS loads only for 2 islands, not entire page

Implementation in Astro

Astro is first framework with native Islands support:

---
// src/pages/blog/[slug].astro
import type { GetStaticPaths } from 'astro';
import { getCollection } from 'astro:content';

// Server components — .astro files with no client JS
import BaseLayout from '@/layouts/BaseLayout.astro';
import ArticleHero from '@/components/ArticleHero.astro';
import Prose from '@/components/Prose.astro';

// Islands — React/Vue/Svelte components with directives
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">
    <!-- Island 1: interactive table of contents -->
    <!-- Loads only when browser idle -->
    <TableOfContents headings={headings} client:idle />

    <article>
      <Prose>
        <Content />
      </Prose>
    </article>
  </div>

  <!-- Island 2: share buttons -->
  <!-- Loads only when visible in viewport -->
  <ShareButtons
    url={Astro.url.href}
    title={post.data.title}
    client:visible
  />

  <!-- Island 3: comments -->
  <!-- Loads only when visible in viewport, 500ms delay -->
  <CommentSection
    articleId={post.id}
    client:visible={{ rootMargin: '0px 0px 200px 0px' }}
  />

  <!-- Island 4: newsletter signup -->
  <!-- Loads immediately — in fold -->
  <NewsletterSignup client:load />
</BaseLayout>

Inter-island communication

Islands are isolated — they have no shared React context. For communication use:

Nano Stores (recommended for Astro):

// 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 }]);
  }
}
// islands/CartIcon.tsx — React island
import { useStore } from '@nanostores/react';
import { cartCount } from '@/stores/cart';

export function CartIcon() {
  const count = useStore(cartCount);
  return (
    <a href="/cart" className="relative">
      <ShoppingCartIcon />
      {count > 0 && <span className="badge">{count}</span>}
    </a>
  );
}
<!-- islands/AddToCartButton.svelte — Svelte island -->
<script>
  import { addToCart } from '@/stores/cart';
  export let product;

  let loading = false;

  async function handleAdd() {
    loading = true;
    addToCart(product);
    loading = false;
  }
</script>

<button on:click={handleAdd} disabled={loading}>
  {loading ? 'Adding...' : 'Add to cart'}
</button>

Both islands (React and Svelte) work with one store. Change in one immediately reflects in other.

Native browser events:

// Universal event bus via CustomEvent
export function emit<T>(event: string, detail: T) {
  window.dispatchEvent(new CustomEvent(event, { detail, bubbles: true }));
}

export function on<T>(event: string, handler: (detail: T) => void) {
  const wrapped = (e: CustomEvent<T>) => handler(e.detail);
  window.addEventListener(event, wrapped as EventListener);
  return () => window.removeEventListener(event, wrapped as EventListener);
}

// In island 1
emit('product:added', { id: product.id, name: product.name });

// In island 2
useEffect(() => {
  return on<{ id: string; name: string }>('product:added', ({ name }) => {
    showToast(`${name} added to cart`);
  });
}, []);

Rendering islands on multiple frameworks

Astro supports multiple renderer plugins simultaneously:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import vue from '@astrojs/vue';
import svelte from '@astrojs/svelte';
import preact from '@astrojs/preact';

export default defineConfig({
  integrations: [
    react(),           // For existing React components
    vue(),             // For Vue components from library
    svelte(),          // For light interactive widgets
    preact({ compat: true }), // Preact as React replacement for light islands
  ],
});

In practice: different frameworks for different purposes within one site without conflicts.

Performance: numbers

Measurements on real projects after Islands Architecture migration:

Metric Before (Full React SSR) After (Astro Islands)
Initial JS 180–250 KB 15–40 KB
TTI 3.2s 0.8s
TBT 480ms 60ms
Lighthouse Performance 62 96

Variance depends on island count and complexity.

Pattern limitations

Islands Architecture doesn't fit:

  • SPA applications — need unified state across many components
  • Dashboards — too much interactivity, islands lose isolation
  • Pages where everything is interactive — minimal benefit

Ideal case: content site with few interactive blocks per page.

Implementation timeline

  • Week 1–2: audit existing site, identify interactive components, setup Astro + renderer plugins
  • Week 3: migrate static pages, split into islands with hydration directives
  • Week 4: inter-island communication via nano stores, test isolation
  • Week 5: measure Core Web Vitals, compare with baseline, optimize directives
  • Week 6: deployment (Cloudflare Pages / Netlify), Lighthouse CI, final documentation