Advanced Frontend Architecture Patterns
1. Micro-Frontends (Module Federation, Webpack 5, Single-SPA)
Split large applications into independently deployable micro-frontends that can be developed, tested, and deployed by separate teams.
| Approach | Technology | Benefits | Challenges |
|---|---|---|---|
| Module Federation | Webpack 5 | Runtime integration, shared dependencies | Complex configuration |
| Iframe Approach | Native iframe | Complete isolation, simple | Poor performance, communication overhead |
| Web Components | Custom Elements | Framework agnostic, standards-based | Styling isolation issues |
| Single-SPA | Single-SPA framework | Framework agnostic orchestration | Learning curve, complexity |
Example: Webpack Module Federation setup
// Host app (shell) - webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
// Load remote apps
app1: 'app1@http://localhost:3001/remoteEntry.js',
app2: 'app2@http://localhost:3002/remoteEntry.js'
},
shared: {
// Share dependencies
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' }
}
})
]
};
// Remote app 1 - webpack.config.js
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
// Expose components
'./Button': './src/components/Button',
'./Dashboard': './src/pages/Dashboard'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
]
};
// Host app - Load remote component
import React, { lazy, Suspense } from 'react';
const RemoteDashboard = lazy(() => import('app1/Dashboard'));
const RemoteButton = lazy(() => import('app1/Button'));
function App() {
return (
<div>
<h1>Host Application</h1>
<Suspense fallback="Loading Dashboard...">
<RemoteDashboard />
</Suspense>
<Suspense fallback="Loading Button...">
<RemoteButton />
</Suspense>
</div>
);
}
Example: Vite Module Federation with plugin
// Installation
npm install -D @originjs/vite-plugin-federation
// Host app - vite.config.ts
import { defineConfig } from 'vite';
import federation from '@originjs/vite-plugin-federation';
export default defineConfig({
plugins: [
federation({
name: 'host-app',
remotes: {
remoteApp: 'http://localhost:3001/assets/remoteEntry.js'
},
shared: ['react', 'react-dom']
})
],
build: {
target: 'esnext',
minify: false
}
});
// Remote app - vite.config.ts
export default defineConfig({
plugins: [
federation({
name: 'remote-app',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button',
'./Header': './src/components/Header'
},
shared: ['react', 'react-dom']
})
],
build: {
target: 'esnext'
}
});
// Host usage
import { lazy } from 'react';
const RemoteButton = lazy(() => import('remoteApp/Button'));
function App() {
return <RemoteButton />;
}
Example: Communication between micro-frontends
// Shared event bus for communication
// eventBus.ts
class EventBus {
private events: Map<string, Function[]> = new Map();
subscribe(event: string, callback: Function) {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event)!.push(callback);
// Return unsubscribe function
return () => {
const callbacks = this.events.get(event);
if (callbacks) {
const index = callbacks.indexOf(callback);
if (index > -1) callbacks.splice(index, 1);
}
};
}
publish(event: string, data?: any) {
const callbacks = this.events.get(event);
if (callbacks) {
callbacks.forEach(cb => cb(data));
}
}
}
export const eventBus = new EventBus();
// App 1 - Publish event
import { eventBus } from '@shared/eventBus';
function CartComponent() {
const addToCart = (item: Product) => {
eventBus.publish('cart:add', { item, quantity: 1 });
};
return <button onClick={() => addToCart(product)}>Add to Cart</button>;
}
// App 2 - Subscribe to event
import { eventBus } from '@shared/eventBus';
import { useEffect, useState } from 'react';
function CartBadge() {
const [count, setCount] = useState(0);
useEffect(() => {
const unsubscribe = eventBus.subscribe('cart:add', (data) => {
setCount(prev => prev + data.quantity);
});
return unsubscribe;
}, []);
return <div>Cart: {count}</div>;
}
// Alternative: Use CustomEvents for cross-app communication
window.dispatchEvent(new CustomEvent('cart:update', {
detail: { items: 5 }
}));
window.addEventListener('cart:update', (e) => {
console.log('Cart updated:', e.detail);
});
Challenges: Version conflicts in shared dependencies, deployment coordination, testing
complexity,
increased bundle size. Use only for large-scale apps with multiple teams. Consider monorepo first.
2. Web Components and Custom Elements (customElements.define)
Create reusable, framework-agnostic components using Web Components standards (Custom Elements, Shadow DOM, HTML Templates).
| API | Purpose | Use Case | Browser Support |
|---|---|---|---|
| Custom Elements | Define custom HTML tags | Reusable components | Excellent |
| Shadow DOM | Encapsulated styles and DOM | Style isolation | Excellent |
| HTML Templates | Reusable DOM fragments | Component templates | Excellent |
| Slots | Content projection | Flexible layouts | Excellent |
Example: Creating Custom Element with Shadow DOM
// my-button.ts - Custom button component
class MyButton extends HTMLElement {
private _disabled: boolean = false;
constructor() {
super();
// Create Shadow DOM for style encapsulation
this.attachShadow({ mode: 'open' });
}
// Observed attributes - trigger attributeChangedCallback
static get observedAttributes() {
return ['disabled', 'variant'];
}
// Lifecycle: Element added to DOM
connectedCallback() {
this.render();
this.addEventListener('click', this.handleClick);
}
// Lifecycle: Element removed from DOM
disconnectedCallback() {
this.removeEventListener('click', this.handleClick);
}
// Lifecycle: Attribute changed
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
if (name === 'disabled') {
this._disabled = newValue !== null;
}
this.render();
}
handleClick = (e: Event) => {
if (this._disabled) {
e.preventDefault();
e.stopPropagation();
return;
}
// Dispatch custom event
this.dispatchEvent(new CustomEvent('button-click', {
bubbles: true,
composed: true,
detail: { timestamp: Date.now() }
}));
};
render() {
const variant = this.getAttribute('variant') || 'primary';
this.shadowRoot!.innerHTML = `
<style>
:host {
display: inline-block;
}
button {
padding: 12px 24px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: all 0.2s;
}
button.primary {
background: #007acc;
color: white;
}
button.secondary {
background: #6c757d;
color: white;
}
button:hover:not(:disabled) {
opacity: 0.9;
transform: translateY(-1px);
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style>
<button class="${variant}" ?disabled="${this._disabled}">
<slot></slot>
</button>
`;
}
}
// Register custom element
customElements.define('my-button', MyButton);
// Usage in HTML
<my-button variant="primary">Click Me</my-button>
<my-button variant="secondary" disabled>Disabled</my-button>
// Usage in JavaScript
const button = document.createElement('my-button');
button.setAttribute('variant', 'primary');
button.textContent = 'Dynamic Button';
button.addEventListener('button-click', (e) => {
console.log('Clicked at:', e.detail.timestamp);
});
document.body.appendChild(button);
Example: Web Component with Lit library (simplifies creation)
// Installation
npm install lit
// my-card.ts
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
@customElement('my-card')
export class MyCard extends LitElement {
@property({ type: String }) title = '';
@property({ type: String }) subtitle = '';
@property({ type: Boolean }) loading = false;
// Scoped styles
static styles = css`
:host {
display: block;
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
background: white;
}
.title {
font-size: 20px;
font-weight: bold;
margin-bottom: 8px;
}
.subtitle {
color: #666;
font-size: 14px;
}
.loading {
opacity: 0.6;
}
`;
render() {
return html`
<div class="${this.loading ? 'loading' : ''}">
<div class="title">${this.title}</div>
<div class="subtitle">${this.subtitle}</div>
<slot></slot>
</div>
`;
}
}
// Usage
<my-card title="Card Title" subtitle="Card subtitle">
<p>Card content goes here</p>
</my-card>
// React integration
function App() {
return (
<my-card
title="From React"
subtitle="Web Component in React"
>
<p>Content</p>
</my-card>
);
}
Example: Slots for content projection
// dialog-box.ts
class DialogBox extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot!.innerHTML = `
<style>
.dialog {
border: 1px solid #ccc;
border-radius: 8px;
padding: 20px;
background: white;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.header {
font-size: 20px;
font-weight: bold;
margin-bottom: 12px;
}
.content {
margin-bottom: 16px;
}
.footer {
display: flex;
justify-content: flex-end;
gap: 8px;
}
</style>
<div class="dialog">
<div class="header">
<slot name="header">Default Header</slot>
</div>
<div class="content">
<slot>Default content</slot>
</div>
<div class="footer">
<slot name="footer"></slot>
</div>
</div>
`;
}
}
customElements.define('dialog-box', DialogBox);
// Usage with named slots
<dialog-box>
<span slot="header">Confirm Action</span>
<p>Are you sure you want to delete this item?</p>
<div slot="footer">
<button>Cancel</button>
<button>Confirm</button>
</div>
</dialog-box>
When to Use: Design systems shared across multiple frameworks, embeddable widgets, library
components.
Lit, Stencil, or vanilla Web Components all work. Full framework agnosticism with standards-based approach.
3. Islands Architecture (Astro, Qwik, Partial Hydration)
Ship zero JavaScript by default, hydrate interactive components only when needed for optimal performance.
| Framework | Approach | Hydration Strategy | Best For |
|---|---|---|---|
| Astro | Islands Architecture | Partial, on-demand | Content sites, blogs, docs |
| Qwik | Resumability | No hydration, resume on interaction | Web apps, e-commerce |
| Next.js Islands | Server Components | Selective client components | Full-stack apps |
| Marko | Automatic partial hydration | Component-level | eBay's solution |
Example: Astro Islands Architecture
// Installation
npm create astro@latest
// src/pages/index.astro
---
import Layout from '../layouts/Layout.astro';
import Counter from '../components/Counter.jsx'; // React component
import Carousel from '../components/Carousel.vue'; // Vue component
---
<Layout title="Astro Islands">
<h1>Welcome to Astro</h1>
<!-- Static content - no JS shipped -->
<p>This content is completely static.</p>
<!-- Island 1: Load immediately -->
<Counter client:load />
<!-- Island 2: Load when visible -->
<Carousel client:visible />
<!-- Island 3: Load on idle -->
<Comments client:idle />
<!-- Island 4: Load on media query -->
<MobileMenu client:media="(max-width: 768px)" />
<!-- No client directive = zero JS -->
<StaticComponent />
</Layout>
// Hydration directives:
// client:load - Load immediately
// client:idle - Load when browser is idle
// client:visible - Load when scrolled into view
// client:media - Load based on media query
// client:only - Only run on client (skip SSR)
// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import vue from '@astrojs/vue';
import svelte from '@astrojs/svelte';
export default defineConfig({
integrations: [react(), vue(), svelte()],
output: 'static' // or 'server' for SSR
});
Example: Qwik resumability (no hydration)
// Installation
npm create qwik@latest
// src/routes/index.tsx
import { component$, useSignal } from '@builder.io/qwik';
export default component$(() => {
const count = useSignal(0);
return (
<div>
<h1>Qwik Counter</h1>
<p>Count: {count.value}</p>
{/* Event handlers are lazy-loaded on interaction */}
<button onClick$={() => count.value++}>
Increment
</button>
</div>
);
});
// Key concepts:
// 1. $ suffix - Qwik optimizer boundary
// 2. useSignal - Reactive state
// 3. No hydration - app "resumes" from server state
// 4. Lazy load event handlers only when needed
// src/components/product-list.tsx
import { component$, Resource, useResource$ } from '@builder.io/qwik';
export default component$(() => {
// Data fetching with suspense
const products = useResource$(async () => {
const res = await fetch('/api/products');
return res.json();
});
return (
<Resource
value={products}
onPending={() => <div>Loading...</div>}
onRejected={(error) => <div>Error: {error.message}</div>}
onResolved={(data) => (
<ul>
{data.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
)}
/>
);
});
// Performance benefits:
// - Instant Time to Interactive (TTI)
// - Progressive interactivity
// - Optimal Core Web Vitals
Example: Astro with React islands for interactivity
// src/components/InteractiveCart.jsx (React)
import { useState } from 'react';
export default function InteractiveCart({ initialItems = [] }) {
const [items, setItems] = useState(initialItems);
const addItem = (item) => {
setItems([...items, item]);
};
const removeItem = (id) => {
setItems(items.filter(item => item.id !== id));
};
return (
<div className="cart">
<h2>Cart ({items.length})</h2>
<ul>
{items.map(item => (
<li key={item.id}>
{item.name} - ${item.price}
<button onClick={() => removeItem(item.id)}>Remove</button>
</li>
))}
</ul>
</div>
);
}
// src/pages/shop.astro
---
import Layout from '../layouts/Layout.astro';
import InteractiveCart from '../components/InteractiveCart.jsx';
import ProductCard from '../components/ProductCard.astro';
const products = await fetch('https://api.example.com/products').then(r => r.json());
---
<Layout title="Shop">
<!-- Static product grid (no JS) -->
<div class="products">
{products.map(product => (
<ProductCard product={product} />
))}
</div>
<!-- Interactive cart (JS island, loads when visible) -->
<InteractiveCart client:visible initialItems={[]} />
</Layout>
// Result: Only cart component ships JavaScript
// Products are pure HTML/CSS = instant rendering
Performance Impact: Islands reduce JavaScript by 90%+. Perfect for content-heavy sites.
Use Astro for static sites with islands of interactivity. Use Qwik for full web apps with optimal performance.
4. React Server Components (RSC) - Next.js 13+
React Server Components render on server, reducing client bundle size and enabling direct backend access without APIs.
| Component Type | Rendering | Capabilities | Use Case |
|---|---|---|---|
| Server Component | Server only | Direct DB access, no client JS | Static content, data fetching |
| Client Component | Client + Server | Hooks, events, browser APIs | Interactive UI |
| Shared Component | Both (default) | Limited to serializable props | Reusable components |
Example: Next.js App Router with Server Components
// app/page.tsx - Server Component (default)
import { db } from '@/lib/database';
import ProductList from './ProductList';
// This runs ONLY on the server
export default async function HomePage() {
// Direct database access (no API route needed)
const products = await db.product.findMany({
include: { category: true }
});
// Can use Node.js APIs
const fs = require('fs');
const data = fs.readFileSync('./data.json', 'utf8');
return (
<div>
<h1>Products</h1>
<ProductList products={products} />
</div>
);
}
// No JavaScript shipped for this component!
// app/ProductList.tsx - Client Component
'use client'; // Directive to mark as client component
import { useState } from 'react';
export default function ProductList({ products }) {
const [filter, setFilter] = useState('');
const filtered = products.filter(p =>
p.name.toLowerCase().includes(filter.toLowerCase())
);
return (
<div>
<input
type="text"
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter products..."
/>
<ul>
{filtered.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
}
// This ships JavaScript for interactivity
Example: Data fetching patterns with Server Components
// app/dashboard/page.tsx
import { Suspense } from 'react';
import UserStats from './UserStats';
import RecentOrders from './RecentOrders';
import Analytics from './Analytics';
export default function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
{/* Stream components independently */}
<Suspense fallback={<div>Loading stats...</div>}>
<UserStats />
</Suspense>
<Suspense fallback={<div>Loading orders...</div>}>
<RecentOrders />
</Suspense>
<Suspense fallback={<div>Loading analytics...</div>}>
<Analytics />
</Suspense>
</div>
);
}
// app/dashboard/UserStats.tsx - Server Component
async function UserStats() {
// Each component fetches its own data
const stats = await fetch('https://api.example.com/stats', {
// Next.js extended fetch with caching
next: { revalidate: 60 } // Revalidate every 60 seconds
}).then(r => r.json());
return (
<div className="stats">
<div>Users: {stats.users}</div>
<div>Active: {stats.active}</div>
</div>
);
}
// app/dashboard/RecentOrders.tsx
async function RecentOrders() {
const orders = await db.order.findMany({
take: 10,
orderBy: { createdAt: 'desc' }
});
return (
<ul>
{orders.map(order => (
<li key={order.id}>
Order #{order.id} - ${order.total}
</li>
))}
</ul>
);
}
// Parallel data fetching with streaming
// Each Suspense boundary resolves independently
Example: Mixing Server and Client Components
// app/product/[id]/page.tsx - Server Component
import { db } from '@/lib/database';
import AddToCartButton from './AddToCartButton';
import RelatedProducts from './RelatedProducts';
export default async function ProductPage({ params }) {
const product = await db.product.findUnique({
where: { id: params.id },
include: { reviews: true }
});
return (
<div>
{/* Server-rendered content */}
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>Price: ${product.price}</p>
{/* Client component for interactivity */}
<AddToCartButton productId={product.id} />
{/* Server component for related products */}
<RelatedProducts categoryId={product.categoryId} />
{/* Server-rendered reviews */}
<div>
<h2>Reviews</h2>
{product.reviews.map(review => (
<div key={review.id}>
<strong>{review.author}</strong>
<p>{review.content}</p>
</div>
))}
</div>
</div>
);
}
// app/product/[id]/AddToCartButton.tsx - Client Component
'use client';
import { useState } from 'react';
export default function AddToCartButton({ productId }) {
const [loading, setLoading] = useState(false);
const handleAdd = async () => {
setLoading(true);
await fetch('/api/cart', {
method: 'POST',
body: JSON.stringify({ productId })
});
setLoading(false);
};
return (
<button onClick={handleAdd} disabled={loading}>
{loading ? 'Adding...' : 'Add to Cart'}
</button>
);
}
// Rules:
// 1. Server components can import client components
// 2. Client components CANNOT import server components directly
// 3. Pass server components as children to client components
Limitations: Cannot use hooks, event handlers, or browser APIs in Server Components. Props must
be
serializable (no functions). Requires Next.js 13+ App Router. Learning curve for new mental model.
5. Edge Computing and Edge Functions (Vercel Edge, Cloudflare Workers)
Deploy serverless functions to edge locations globally for ultra-low latency API responses and dynamic content.
| Platform | Runtime | Cold Start | Use Case |
|---|---|---|---|
| Vercel Edge Functions | V8 isolates | <50ms | API routes, middleware |
| Cloudflare Workers | V8 isolates | <5ms | Edge compute, KV storage |
| AWS Lambda@Edge | Node.js | ~100ms | CloudFront customization |
| Netlify Edge Functions | Deno | <50ms | JAMstack edge logic |
Example: Vercel Edge Function (Next.js)
// app/api/edge/route.ts
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge'; // Enable Edge Runtime
export async function GET(request: NextRequest) {
// Access geo location
const country = request.geo?.country || 'Unknown';
const city = request.geo?.city || 'Unknown';
// Fast external API call (co-located at edge)
const data = await fetch('https://api.example.com/data', {
headers: { 'Authorization': process.env.API_KEY }
}).then(r => r.json());
return NextResponse.json({
message: `Hello from ${city}, ${country}!`,
data,
edge: true
});
}
// Edge middleware - runs before every request
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Geo-based redirect
const country = request.geo?.country;
if (country === 'CN' && !request.nextUrl.pathname.startsWith('/cn')) {
return NextResponse.redirect(new URL('/cn', request.url));
}
// A/B testing
const bucket = Math.random() < 0.5 ? 'a' : 'b';
const response = NextResponse.next();
response.cookies.set('ab-test', bucket);
// Add custom headers
response.headers.set('x-edge-location', request.geo?.city || 'Unknown');
return response;
}
export const config = {
matcher: ['/api/:path*', '/products/:path*']
};
Example: Cloudflare Workers with KV storage
// worker.js
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const url = new URL(request.url);
// Check cache first
const cache = caches.default;
let response = await cache.match(request);
if (response) {
return response;
}
// Access KV storage (ultra-fast key-value store)
const config = await MY_KV_NAMESPACE.get('config', { type: 'json' });
// Generate response
response = new Response(JSON.stringify({
message: 'Hello from Cloudflare Edge',
config,
location: request.cf?.city
}), {
headers: {
'content-type': 'application/json',
'cache-control': 'public, max-age=60'
}
});
// Cache response
event.waitUntil(cache.put(request, response.clone()));
return response;
}
// wrangler.toml
name = "my-worker"
main = "worker.js"
compatibility_date = "2024-01-01"
[[kv_namespaces]]
binding = "MY_KV_NAMESPACE"
id = "your-namespace-id"
// Deploy: npx wrangler deploy
Example: Edge function for personalization
// app/api/personalize/route.ts
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge';
export async function GET(request: NextRequest) {
// Get user preferences from cookie
const preferences = request.cookies.get('preferences')?.value;
const parsed = preferences ? JSON.parse(preferences) : {};
// Geo-based content
const country = request.geo?.country || 'US';
const currency = getCurrency(country);
// A/B test variant
const variant = request.cookies.get('ab-variant')?.value || 'control';
// Time-based personalization
const hour = new Date().getHours();
const greeting = hour < 12 ? 'Good morning' :
hour < 18 ? 'Good afternoon' : 'Good evening';
return NextResponse.json({
greeting,
currency,
variant,
preferences: parsed,
location: {
country,
city: request.geo?.city
}
});
}
function getCurrency(country: string): string {
const currencyMap: Record<string, string> = {
US: 'USD',
GB: 'GBP',
JP: 'JPY',
DE: 'EUR'
};
return currencyMap[country] || 'USD';
}
// Usage in page
async function ProductPrice({ productId }) {
const personalization = await fetch('/api/personalize').then(r => r.json());
const price = await getPrice(productId, personalization.currency);
return <div>{price}</div>;
}
Edge Benefits: 50-200ms faster than traditional serverless, global distribution, lower costs at
scale.
Limitations: No Node.js APIs, limited CPU time (50ms), smaller bundle size. Perfect for API routes, middleware,
auth.
6. WebAssembly (WASM) Integration for Performance
Run high-performance compiled code (C, C++, Rust) in the browser at near-native speed for compute-intensive tasks.
| Language | Toolchain | Use Case | Performance |
|---|---|---|---|
| Rust | wasm-pack, wasm-bindgen | CPU-intensive algorithms | Near-native |
| C/C++ | Emscripten | Porting existing libraries | Near-native |
| AssemblyScript | asc (TypeScript-like) | TypeScript developers | Very fast |
| Go | TinyGo | Small WASM binaries | Fast |
Example: Rust WebAssembly module with wasm-pack
// Installation
cargo install wasm-pack
// Create new Rust project
cargo new --lib image-processor
cd image-processor
// Cargo.toml
[package]
name = "image-processor"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
// src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u32 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2)
}
}
#[wasm_bindgen]
pub struct ImageProcessor {
width: u32,
height: u32,
}
#[wasm_bindgen]
impl ImageProcessor {
#[wasm_bindgen(constructor)]
pub fn new(width: u32, height: u32) -> ImageProcessor {
ImageProcessor { width, height }
}
pub fn grayscale(&self, pixels: &mut [u8]) {
for chunk in pixels.chunks_mut(4) {
let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3;
chunk[0] = avg as u8;
chunk[1] = avg as u8;
chunk[2] = avg as u8;
}
}
pub fn blur(&self, pixels: &mut [u8], radius: u32) {
// High-performance blur algorithm
// ... implementation
}
}
// Build WASM
// wasm-pack build --target web
// JavaScript usage
// src/App.tsx
import init, { fibonacci, ImageProcessor } from './wasm/image_processor';
async function App() {
// Initialize WASM module
await init();
// Use Rust functions
const result = fibonacci(10);
console.log('Fibonacci(10):', result);
// Process image
const processor = new ImageProcessor(800, 600);
const imageData = ctx.getImageData(0, 0, 800, 600);
processor.grayscale(imageData.data);
ctx.putImageData(imageData, 0, 0);
return <div>WASM Loaded</div>;
}
Example: AssemblyScript for TypeScript developers
// Installation
npm install -D assemblyscript
// assembly/index.ts (AssemblyScript - TypeScript-like)
export function add(a: i32, b: i32): i32 {
return a + b;
}
export function sortArray(arr: Int32Array): void {
// Efficient sorting in WASM
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
export function processMatrix(
matrix: Float64Array,
rows: i32,
cols: i32
): Float64Array {
const result = new Float64Array(rows * cols);
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
const idx = i * cols + j;
result[idx] = matrix[idx] * 2.0;
}
}
return result;
}
// Build: npm run asbuild
// JavaScript usage
import { add, sortArray } from './build/release.js';
const result = add(5, 10);
console.log(result); // 15
const arr = new Int32Array([5, 2, 8, 1, 9]);
sortArray(arr);
console.log(arr); // [1, 2, 5, 8, 9]
Example: Real-world WASM use case - Image compression
// imageCompressor.ts - React component using WASM
import { useEffect, useState } from 'react';
import init, { compress_image } from './wasm/image_compressor';
export function ImageCompressor() {
const [wasmReady, setWasmReady] = useState(false);
useEffect(() => {
init().then(() => setWasmReady(true));
}, []);
const handleCompress = async (file: File) => {
if (!wasmReady) return;
const arrayBuffer = await file.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
// Call Rust WASM function
const compressed = compress_image(uint8Array, 80); // 80% quality
// Create download link
const blob = new Blob([compressed], { type: 'image/jpeg' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'compressed.jpg';
a.click();
};
return (
<div>
{wasmReady ? (
<input
type="file"
accept="image/*"
onChange={(e) => handleCompress(e.target.files[0])}
/>
) : (
<p>Loading WASM module...</p>
)}
</div>
);
}
// Performance comparison:
// JavaScript image compression: ~2000ms
// WASM image compression: ~200ms
// 10x faster!
Advanced Architecture Decision Matrix
| Pattern | When to Use | Benefits | Complexity |
|---|---|---|---|
| Micro-Frontends | Large orgs, multiple teams, gradual migration | Independent deployment, tech diversity | High |
| Web Components | Design systems, framework-agnostic libraries | Standards-based, reusable everywhere | Medium |
| Islands Architecture | Content sites with selective interactivity | Minimal JS, excellent performance | Low-Medium |
| Server Components | Data-heavy apps, reducing client bundle | Zero client JS, direct backend access | Medium |
| Edge Computing | Global apps, low-latency requirements | Ultra-fast responses, geo-distribution | Low-Medium |
| WebAssembly | CPU-intensive tasks, porting existing code | Near-native performance | High |