komal.nakrani
Browse Themes
ThemesCategoriesDocsBlogTips & Tricks
Components & Islands••By Komal Nakrani

Astro Island vs Script: Framework Island or Plain Script?

Astro island vs script: use a plain script for toggles, tabs and copy buttons; use a framework island when state is complex, shared or already built in React.

#Astro#Decision Guide#Islands#JavaScript

The Astro island vs script choice is about how much state a component has. For interactions where the HTML barely changes (opening a menu, switching tabs, copying text, toggling dark mode), a <script> in an Astro component is smaller and simpler. When the UI re-renders from data (filters that update a list, multi-step forms, live search, anything with lots of state), a framework island with React, Svelte, Vue, Solid or Preact pays for its runtime.

Key Takeaways

  • Script: a few hundred bytes, no runtime, works on every page that renders the component.
  • Island: a framework runtime plus your code, but declarative state and re-rendering.
  • If you already have the component in React, using it as an island is usually fine.
  • Two or three small islands from one framework cost one runtime; a second framework doubles it.

Decision flow

  1. Does the interaction only toggle attributes or classes? (menu, accordion, tabs, dialog, copy) → script.
  2. Does it fetch data and render a list or results? → island (or a script, if the list is simple).
  3. Does it have several pieces of state that depend on each other? (filters, wizard, cart editor) → island.
  4. Does an existing, tested component already exist in a framework? → island; don’t rewrite working code.
  5. Will many instances appear on one page? → script, which Astro includes once regardless of instance count.

Compared

Script in .astro Framework island
Bundle Your code only Framework runtime + your code
Server HTML Yes, the component’s markup Yes (except client:only)
State Manual: DOM attributes, variables Declarative
Re-rendering You update the DOM Framework does it
Load timing Deferred module after parse Controlled by client:*
Many instances Script included once Each instance hydrates
Testing DOM tests Framework testing tools

The same accordion both ways

As a script:

---
// src/components/Accordion.astro
const { title } = Astro.props;
---
<div class="accordion">
  <button class="accordion-trigger" aria-expanded="false">{title}</button>
  <div class="accordion-panel" hidden><slot /></div>
</div>

<script>
  document.querySelectorAll<HTMLButtonElement>('.accordion-trigger').forEach((btn) => {
    btn.addEventListener('click', () => {
      const open = btn.getAttribute('aria-expanded') === 'true';
      btn.setAttribute('aria-expanded', String(!open));
      (btn.nextElementSibling as HTMLElement).hidden = open;
    });
  });
</script>

As a React island:

// src/components/Accordion.tsx
import { useState, type ReactNode } from 'react';

export default function Accordion({ title, children }: { title: string; children: ReactNode }) {
  const [open, setOpen] = useState(false);
  return (
    <div className="accordion">
      <button aria-expanded={open} onClick={() => setOpen(!open)}>{title}</button>
      <div hidden={!open}>{children}</div>
    </div>
  );
}
<Accordion client:visible title="Shipping">...</Accordion>

Both are accessible and behave the same. The script version ships no framework; the island ships React. For an accordion, the script wins. For a product filter with ten checkboxes driving a results list, the island’s state model is worth it.

A middle path

Libraries that enhance HTML without a virtual DOM, such as Alpine.js, or web components, sit between the two. They work in Astro with a <script> import and suit teams who want declarative markup without a full framework.

Directive choice for islands is covered in which client directive to use, and the cost of each island in finding what’s bloating your Astro bundle. The Astro components guide covers both patterns, and the official client-side scripts guide explains script processing.

FAQ

Do scripts in Astro components run once per component instance?

The script is included once per page. Write it to find and set up every instance, as in the accordion example.

Can a script and an island share state?

Yes, through the DOM (custom events, data attributes) or a framework-neutral store such as Nano Stores.