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

Astro Directives Cheatsheet

All 16 Astro directives on one page: client:* hydration, server:defer, set:html, class:list, define:vars, is:inline, is:global and transition:*.

#Astro#Cheatsheet#Directives#Islands

Astro directives are special attributes, written with a colon, that change how Astro renders an element or component. There are 16 in Astro 7: five client:* hydration directives, server:defer for server islands, set:html and set:text for raw content, class:list for classes, define:vars for passing values to CSS and scripts, three is:* directives that change processing, and three transition:* directives for view transitions. This page lists them all with the one-line rule and an example for each.

Key Takeaways

  • client:* directives only work on framework components (React, Vue, Svelte, Solid, Preact).
  • server:defer only works on Astro components and needs an adapter.
  • set:html inserts unescaped HTML; never pass it untrusted input.
  • is:inline opts a script or style out of Astro’s processing entirely.

Hydration: client:*

Directive Hydrates Use for
client:load Immediately on page load Above-the-fold controls
client:idle When the browser is idle (requestIdleCallback) Important but not urgent widgets
client:visible When the component enters the viewport Anything below the fold
client:media="(query)" When a media query matches Mobile-only or desktop-only UI
client:only="framework" In the browser only, no server render Components that can’t run on the server
<Counter client:load />
<SearchBox client:idle={{ timeout: 500 }} />
<Testimonials client:visible={{ rootMargin: '200px' }} />
<MobileNav client:media="(max-width: 768px)" />
<Map client:only="react" />

No directive means the component renders to HTML and ships no JavaScript. Choosing well is covered in lazy-loading islands.

Server islands: server:defer

<UserMenu server:defer>
  <a slot="fallback" href="/login/">Sign in</a>
</UserMenu>

Renders the component on the server per request, after the page loads. The page itself can stay static and cached. Requires an adapter; without one you’ll see Cannot use server islands without an adapter.

Content: set:html and set:text

<article set:html={post.renderedHtml} />
<Fragment set:html={svgString} />
<script type="application/ld+json" set:html={JSON.stringify(schema)} />

<p set:text={userInput} />

set:html injects a string without escaping. set:text sets escaped text content, like {value}. On <Fragment>, set:html renders with no wrapper element. Details in Fragment and set:html.

Classes: class:list

<a class:list={['btn', `btn-${variant}`, { active: isActive }, className]}>...</a>

Accepts strings, arrays and objects, flattens them, drops falsy values and removes duplicates. The cleanest way to combine a base class, variants and a caller’s class prop.

Values into CSS and scripts: define:vars

---
const { accent = '#2563eb' } = Astro.props;
---
<style define:vars={{ accent }}>
  .badge { background: var(--accent); }
</style>

Each key becomes a CSS custom property scoped to the component instance. On a <script>, define:vars makes the script inline (unbundled). See define:vars for the trade-offs.

Processing control: is:*

Directive On Effect
is:global <style> Styles are not scoped to the component
is:inline <script>, <style> Left exactly as written: no bundling, no deduplication, no TypeScript
is:raw Any element Children are treated as raw text, not Astro syntax
<style is:global>
  body { margin: 0; }
</style>

<script is:inline>
  document.documentElement.dataset.theme = localStorage.getItem('theme') ?? 'light';
</script>

<code is:raw>{this is shown literally}</code>

is:inline is the right choice for the dark-mode script that must run before paint, and for third-party snippets. For everything else, processed scripts are smaller and deduplicated.

View transitions: transition:*

Directive Effect
transition:name="hero-image" Pairs elements across pages so they animate into each other
transition:animate="fade" Sets the animation: fade, slide, none, initial or a custom one
transition:persist Keeps the element (and island state) across navigations
<img src={cover} alt="" transition:name={`cover-${post.id}`} />
<main transition:animate="slide"><slot /></main>
<AudioPlayer client:load transition:persist />

These take effect with <ClientRouter /> from astro:transitions in the page head. transition:name also works with native cross-document view transitions in supporting browsers.

Which component types accept which directives

Directive Astro component Framework component HTML element
client:* No Yes No
server:defer Yes No No
set:html, set:text No No Yes (and Fragment)
class:list Passed as prop Passed as prop Yes
define:vars No No <style>, <script>
is:* No No Yes
transition:* Yes Yes Yes

Common mistakes

  • client:only without a value fails: write client:only="react". See Missing hint on client:only.
  • client:media without a query fails. See Missing value for client:media.
  • A client:* directive on an .astro component does nothing useful; Astro components don’t hydrate.
  • onclick={handler} on an HTML element in an Astro template doesn’t attach your frontmatter function; use a <script>.

For how these fit into components, read the Astro components guide. The formal definitions are in the directives reference.

FAQ

What is the default hydration directive in Astro?

There isn’t one. Without a client:* directive, a framework component renders to static HTML and ships no JavaScript.

Can I combine client:visible and transition:persist?

Yes. Directives from different groups can be combined on the same component.