Skip to content

Components & Islands

Astro has two kinds of components that are easy to confuse:

  • Astro components, written as .astro files, are the default for HTML-first UI.
  • Framework components, such as React or Vue components, are for parts of the page that need browser state or browser APIs.

The distinction matters because importing a component does not automatically mean Astro ships a client application. Static UI can stay static. Interactive UI can hydrate as an island.

An Astro component has frontmatter and a template:

---
const { title, tone = 'default' } = Astro.props;
---
<section class:list={['callout', `callout-${tone}`]}>
<h2>{title}</h2>
<slot />
</section>
<style>
.callout {
border: 1px solid var(--sl-color-gray-5);
border-radius: 0.5rem;
padding: 1rem;
}
</style>

The frontmatter runs during the build or on the server. The template becomes HTML. Styles inside the component are scoped by default.

Use Astro components for content wrappers, layout sections, cards, callouts, navigation surfaces, metadata helpers, and anything that does not need browser behavior.

Props pass data into a component. Slots pass content into a component.

---
import Callout from '../components/Callout.astro';
---
<Callout title="Fast by default" tone="info">
<p>Astro renders HTML first and hydrates only what you choose.</p>
</Callout>

This pattern is useful in documentation because the component provides structure while the page keeps readable Markdown-like content.

Named slots help when a component needs more than one content region:

<FeatureCard>
<span slot="eyebrow">Routing</span>
<h2>Files become URLs</h2>
<p>Keep the route tree visible in the filesystem.</p>
</FeatureCard>

Do not turn every paragraph into a component. Components are useful when they remove real repetition or make structure clearer.

Use a framework component when the UI needs browser behavior:

  • a combobox
  • a live chart
  • a filter panel
  • drag and drop
  • media controls
  • a code playground
  • an authenticated widget

Astro supports React, Vue, Svelte, Solid, and other frameworks through integrations. A project can use more than one framework, but that is rarely the simplest choice. Each framework adds dependencies, conventions, and bundle cost.

A framework component only becomes interactive in the browser when you add a client directive:

---
import SearchBox from '../components/SearchBox.jsx';
---
<SearchBox client:visible />

Common directives:

DirectiveBehaviorGood fit
client:loadHydrates as soon as possibleAbove-the-fold controls that must work immediately
client:idleHydrates when the browser is idleNice-to-have widgets
client:visibleHydrates when the component enters the viewportDemos and widgets lower on the page
client:mediaHydrates when a media query matchesMobile-only or desktop-only interactions
client:onlyRenders only on the clientComponents that cannot run during server rendering

Choose the least eager directive that still gives readers a good experience. A filter at the top of a page may need client:load. A demo below several sections is usually better with client:visible.

The Islands architecture lets a page remain mostly static while specific parts become interactive. Instead of one large app taking over the document, Astro produces a stable HTML shell with small hydrated islands inside it.

For documentation, this is a natural fit. Readers and search engines get the content immediately. Interactions such as search, theme switching, copy buttons, playgrounds, or charts can load independently.

The result is not automatically fast; a large island can still ship too much JavaScript. The advantage is that Astro gives you a clear boundary where you can ask whether the JavaScript is worth it.

NeedStart with
Repeated static page sectionAstro component
Markdown content with consistent wrapperAstro component or Starlight component
Interactive form controlFramework component with client:load or client:idle
Below-the-fold demoFramework component with client:visible
Browser-only API such as window at render timeclient:only or move browser code into an effect
Site-wide app stateReconsider whether Astro is the right center for that surface
SymptomWhat to check
React component renders but clicks do nothingMissing client:* directive
Build fails because window is undefinedBrowser API is running during server render
Page ships too much JavaScriptIslands are too large or hydrated too eagerly
Component styles leak unexpectedlyGlobal CSS is being used where scoped styles would be safer
Every section becomes a componentThe abstraction is hiding content rather than simplifying it