Skip to content

Directives

Control how framework components (React, Vue, Svelte, etc.) are hydrated on the client.

Hydrate immediately on page load. Highest priority.

<InteractiveNav client:load />

Use for: Navigation, modals, forms, anything above the fold.

Hydrate once the page is done with initial load (uses requestIdleCallback).

<AnalyticsWidget client:idle />

Use for: Low-priority UI that doesn’t need immediate interaction.

Hydrate when the component enters the viewport (uses IntersectionObserver).

<ChartComponent client:visible />

Use for: Below-the-fold content, lazy-loaded widgets.

Can customize the observer:

<HeavyComponent client:visible={{ rootMargin: "200px" }} />

Hydrate when a CSS media query matches.

<MobileMenu client:media="(max-width: 768px)" />

Use for: Components only needed at certain viewport sizes.

Skip server rendering. Render only on the client.

<BrowserOnlyChart client:only="react" />

Must specify the framework: "react", "vue", "svelte", "solid-js", "lit", "preact"

Use for: Components using browser-only APIs (canvas, WebGL, localStorage at render time).

Defer component rendering to after initial page load. The page ships immediately with fallback content, then the component renders on the server and streams in.

<UserProfile server:defer>
<LoadingSkeleton slot="fallback" />
</UserProfile>

Use for: Personalized content, slow data fetches, authenticated content.

Build class strings from arrays, objects, and values:

<div class:list={[
'base-class',
{ active: isActive, hidden: !show },
conditionalClass && 'applied',
]} />

Inject raw HTML (like innerHTML):

<div set:html={rawHtmlString} />

Set text content (safely escaped):

<p set:text={userInput} />

Make <style> apply globally instead of scoped:

<style is:global>
body { margin: 0; }
.prose h1 { font-size: 2rem; }
</style>

Skip processing for script/style tags:

<!-- No bundling, no deduplication -->
<script is:inline>
console.log('Runs as-is for each component instance');
</script>
<style is:inline>
/* Not scoped, not processed */
</style>

Skip Astro template processing for child content:

<div is:raw>
{/* This won't be treated as a JS expression */}
<p>Rendered literally including {curly braces}</p>
</div>

Pass component variables to <script> or <style> tags:

---
const textColor = 'navy';
const message = 'Hello';
---
<style define:vars={{ textColor }}>
h1 { color: var(--textColor); }
</style>
<script define:vars={{ message }}>
console.log(message); // 'Hello'
</script>

Used with View Transitions.

Unique identifier for animating between pages:

<h1 transition:name="page-title">{title}</h1>
<img transition:name={`hero-${id}`} src={image} />

Override the default animation:

<div transition:animate="slide">Slides in</div>
<div transition:animate="fade">Fades in</div>
<div transition:animate="none">No animation</div>

Keep a component alive across page navigations (no re-render):

<AudioPlayer transition:persist />
<video transition:persist />