Extended Play

Astro Islands for Busy Dads: Ship Zero JS by Default

How Astro's Islands Architecture lets SAHD developers build lightning-fast sites without sacrificing interactivity

18 min read
August 7, 2024
astro, performance, javascript, islands

Astro Islands for Busy Dads: Ship Zero JS by Default

Extended Play (33s): Master Astro's Islands Architecture in your next naptime coding session

What You'll Learn

  • Why Islands Architecture is perfect for time-constrained development
  • How to ship zero JavaScript by default while keeping interactivity
  • Practical patterns for adding interactivity only where needed
  • Real-world examples of Islands in SAHD projects

The SAHD Context

As stay-at-home dad developers, we face a unique challenge: we need to ship fast, reliable websites without the luxury of debugging performance issues at 2 AM. Every kilobyte matters when your users are other busy parents on mobile devices with spotty connections.

Traditional JavaScript frameworks force you to choose: either ship a massive bundle or spend hours optimizing. Astro's Islands Architecture gives you a third option: ship zero JavaScript by default, then add interactivity surgically where it's actually needed.

Quick Overview

Quick Win

⏱️ 3 minutes 🟢 easy

If you only have 3 minutes right now, here's the core concept:

Islands Architecture = Static HTML everywhere + Interactive "islands" of JavaScript only where needed

---
// This runs on the server, not the browser
const posts = await fetch('/api/posts').then(r => r.json());
---

<!-- Static HTML - zero JS -->
<h1>My Blog</h1>
<ul>
  {posts.map(post => <li>{post.title}</li>)}
</ul>

<!-- Interactive island - only loads JS here -->
<SearchBox client:load />

Result: 95% of your page is static HTML, 5% is interactive. Perfect for busy parents who need fast sites.

The Problem with Traditional Approaches

Most modern frameworks follow the "JavaScript everywhere" approach:

React/Next.js Challenges

  • Bundle size: Everything becomes JavaScript, even static content
  • Hydration issues: Complex debugging when server/client don't match
  • Performance overhead: React loads even for static pages
  • Complexity: More moving parts = more things that break during interruptions

The SAHD Developer Reality

When you're coding between diaper changes, you need:

  • ✅ Predictable behavior (no hydration surprises)
  • ✅ Fast builds (precious coding time)
  • ✅ Easy debugging (when things break at inconvenient times)
  • ✅ Performance by default (no optimization rabbit holes)

The Astro Islands Solution

Astro flips the script: HTML by default, JavaScript by choice.

How Islands Work

---
// Server-side logic (runs at build time)
const user = await getCurrentUser();
const posts = await getBlogPosts();
---

<!-- Static HTML - loads instantly -->
<header>
  <h1>Welcome, {user.name}</h1>
  <nav>
    <a href="/about">About</a>
    <a href="/blog">Blog</a>
  </nav>
</header>

<main>
  <!-- Still static HTML -->
  <section>
    <h2>Recent Posts</h2>
    {posts.map(post => (
      <article>
        <h3>{post.title}</h3>
        <p>{post.excerpt}</p>
      </article>
    ))}
  </section>
  
  <!-- Island of interactivity -->
  <SearchComponent client:load />
  
  <!-- Another island, loads when visible -->
  <CommentSection client:visible />
</main>

Result: The search component loads ~5KB of JavaScript. Everything else? Pure HTML.

Client Directives: Your Islands Toolkit

🚀
Performance

Performance tip: Use the most restrictive directive possible. client:idle is usually better than client:load

<!-- Load immediately (use sparingly) -->
<ChatWidget client:load />

<!-- Load when browser is idle (best for non-critical) -->
<AnalyticsTracker client:idle />

<!-- Load when element becomes visible (perfect for below-fold) -->
<ImageCarousel client:visible />

<!-- Load on first interaction (great for forms) -->
<ContactForm client:media="(min-width: 640px)" />

<!-- No JavaScript at all (default) -->
<BlogPost />

Real-World SAHD Implementation

Let me show you how I rebuilt my portfolio using Islands:

Before: React/Next.js Approach

// Everything is JavaScript, even static content
export default function Home() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    fetch('/api/posts')
      .then(r => r.json())
      .then(setPosts)
      .finally(() => setLoading(false));
  }, []);
  
  if (loading) return <Spinner />;
  
  return (
    <div>
      <Header />
      <PostList posts={posts} />
      <SearchBox />
      <ContactForm />
    </div>
  );
}

Problems:

  • 🚫 45KB JavaScript bundle (React + my code)
  • 🚫 Loading spinner during hydration
  • 🚫 SEO challenges with client-side rendering
  • 🚫 Complex state management for simple content

After: Astro Islands

---
// Runs at build time - no client-side fetching needed
const posts = await Astro.glob('./posts/*.md');
---

<!DOCTYPE html>
<html>
<head>
  <title>SAHD Developer Portfolio</title>
</head>
<body>
  <!-- Static HTML - loads instantly -->
  <header>
    <h1>Jackson Sande</h1>
    <nav>
      <a href="/about">About</a>
      <a href="/projects">Projects</a>
    </nav>
  </header>
  
  <main>
    <!-- Static content - no JavaScript needed -->
    <section>
      <h2>Recent Posts</h2>
      {posts.map(post => (
        <article>
          <h3>{post.frontmatter.title}</h3>
          <p>{post.frontmatter.description}</p>
          <time>{post.frontmatter.date}</time>
        </article>
      ))}
    </section>
    
    <!-- Islands of interactivity -->
    <SearchBox client:idle />
    <ContactForm client:visible />
  </main>
</body>
</html>

Results:

  • ✅ 3KB JavaScript total (only for search + contact form)
  • ✅ Instant page load (no hydration delay)
  • ✅ Perfect SEO (real HTML from server)
  • ✅ Simple mental model (HTML + targeted JS)
💯

Keeping It Real

👨‍👧‍👦 Every SAHD can relate

The first time I shipped an Astro site, I kept refreshing the browser thinking something was broken. "This can't load this fast," I thought.

Then my 4-year-old walked over and asked to see "daddy's website" on my phone. It loaded instantly, even on our slow WiFi. That's when it clicked: this is what the web should feel like.

No more explaining to family why "daddy's work website" takes forever to load.

Choosing Your Islands

The key to Islands success is knowing when to add interactivity:

Static by Default ✅

  • Blog posts and articles
  • About pages and bios
  • Product catalogs
  • Documentation
  • Landing pages

Islands for Interaction 🏝️

  • Search functionality
  • Contact forms
  • Shopping carts
  • Comment systems
  • Real-time data (stock prices, weather)

Framework Integration

You can use any framework for your islands:

<!-- React island -->
<ReactCounter client:load />

<!-- Vue island -->  
<VueModal client:visible />

<!-- Svelte island -->
<SvelteChart client:idle />

<!-- Vanilla JS island -->
<script>
  // Sometimes vanilla is best
  document.querySelector('#mobile-menu').addEventListener('click', toggle);
</script>

Performance in the Real World

Here's what Islands Architecture means for SAHD projects:

Lighthouse Scores

Before (React SPA):

  • Performance: 67/100
  • First Contentful Paint: 2.1s
  • JavaScript bundle: 45KB

After (Astro Islands):

  • Performance: 98/100
  • First Contentful Paint: 0.8s
  • JavaScript bundle: 3KB

User Experience

  • Mobile users: Pages load on slow connections
  • SEO: Google sees real HTML, not JavaScript
  • Accessibility: Screen readers work immediately
  • Battery life: Less JavaScript = longer battery
Gotcha!

Watch out: Don't add client:load to everything just because you can. Every island adds JavaScript. Be intentional.

Common Patterns for SAHDs

Pattern 1: Progressive Enhancement

Start with HTML, add interactivity:

<!-- Works without JavaScript -->
<form action="/api/contact" method="post">
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

<!-- Enhanced with JavaScript island -->
<FormValidator client:visible />

Pattern 2: Lazy-Loaded Features

Perfect for below-the-fold content:

<!-- Static hero section -->
<section class="hero">
  <h1>Welcome to SAHD.dev</h1>
  <p>Practical code for impractical schedules</p>
</section>

<!-- Interactive newsletter signup (loads when visible) -->
<NewsletterSignup client:visible />

<!-- Comments load when user scrolls down -->
<CommentSection client:visible />

Pattern 3: Media Queries for Islands

Only load JavaScript on devices that need it:

<!-- Mobile-first: no JavaScript -->
<MobileMenu />

<!-- Desktop gets interactive version -->
<DesktopDropdown client:media="(min-width: 768px)" />

Migration Strategy

Moving from SPA to Islands? Do it gradually:

Step 1: Identify Static Content

  • Which pages never change after load?
  • What content comes from your CMS/database?
  • Where do you actually need real-time updates?

Step 2: Start with New Pages

  • Build new features as static-first
  • Use islands only for necessary interactivity
  • Get comfortable with the mental model

Step 3: Convert High-Traffic Pages

  • Homepage and landing pages first
  • Blog posts and documentation
  • Product pages (usually static)

Migration Timeline

Don't rewrite everything at once. I migrated my blog over three weekend coding sessions, one section at a time.

Testing & Validation

Verify your Islands implementation:

# Build and check bundle sizes
npm run build
du -sh dist/_astro/*.js

# Test with JavaScript disabled
# (Your site should still work!)

# Check Lighthouse scores
npx lighthouse http://localhost:4321 --view

Debugging Islands

Common issues and solutions:

Island doesn't load:

<!-- Wrong: component not exported -->
<MyComponent client:load />

<!-- Right: check your imports -->
---
import MyComponent from './MyComponent.jsx';
---
<MyComponent client:load />

State sharing between islands:

// Use global state or browser APIs
localStorage.setItem('theme', 'dark');

// Or nano stores for simple state
import { atom } from 'nanostores';
export const theme = atom('light');

Key Takeaways

  • Performance by default: Start with HTML, add JavaScript intentionally
  • Better user experience: Faster loads, especially on mobile
  • Simpler debugging: Less JavaScript = fewer places for bugs to hide
  • SEO advantages: Real HTML means better search visibility
  • Framework flexibility: Use React, Vue, Svelte - whatever you know
💡
Pro Tip

Time investment: Plan for 2-3 hours to learn Islands, then 30 minutes per page conversion. The performance gains pay off immediately.

What's Next

  • Advanced Islands: Sharing state between islands with nano-stores
  • Server-Side Rendering: When to use SSR vs static generation
  • Deployment: Optimizing Astro builds for Cloudflare Workers

Ready to ship faster websites without the JavaScript overhead? Islands Architecture gives you the performance your users deserve and the simplicity your schedule demands.

Questions About Islands?

Share your Astro experiences or ask about specific use cases in the comments. Every SAHD developer's project is different!

More from SAHD.dev

Check out our personal stories in Singles, or find tools in the Toolbox.