Skip to content
Astro as integrations 4 min read

Integrations & Adapters

Astro ships with a deliberately small core and grows through integrations — packages that hook into the build to add UI frameworks, SSR adapters, tooling, and content features. Because Astro renders to zero JavaScript by default, integrations are how you opt into exactly the capabilities you need, whether that’s an interactive React island, a Tailwind setup, or a Node server adapter for SSR. The astro add command wires almost all of them up in one step, editing your config and installing dependencies for you.

What an integration is

An integration is a JavaScript object (usually returned by a factory function) that taps into Astro’s lifecycle hooks. It can register Vite plugins, inject routes and scripts, add renderers for UI frameworks, or configure a deployment target. You list integrations in the integrations array of astro.config.mjs.

// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://example.com',
  integrations: [react(), sitemap()],
});

Each entry is a call to the integration factory — note the parentheses. Most accept an options object: react({ include: ['**/react/*'] }).

How integrations hook into the build

Integrations subscribe to named hooks that fire at well-defined points. The most common are astro:config:setup (mutate config, add Vite plugins, inject routes), astro:config:done, and astro:build:done (post-build tasks like generating a sitemap). Here is a minimal custom integration:

// my-integration.ts
import type { AstroIntegration } from 'astro';

export default function welcome(): AstroIntegration {
  return {
    name: 'welcome-banner',
    hooks: {
      'astro:config:setup': ({ logger }) => {
        logger.info('Welcome integration loaded');
      },
      'astro:build:done': ({ pages, logger }) => {
        logger.info(`Built ${pages.length} pages`);
      },
    },
  };
}
npm run build

Output:

[welcome-banner] Welcome integration loaded
[welcome-banner] Built 42 pages

Hooks run in the order integrations appear in the array. If two integrations touch the same config, the later one wins — order matters for things like CSS frameworks and renderers.

Adding integrations with astro add

The fastest path is the CLI. It detects your package manager, installs the package, and patches astro.config.mjs automatically.

npx astro add react
npx astro add tailwind sitemap partytown

You can pass several at once. After it edits the config it prints a diff and asks for confirmation. The result is identical to editing the file by hand, but without import typos.

Run astro add from your project root with a clean git tree so you can review the generated diff before committing.

Integrations vs adapters

Both are technically integrations, but they serve different purposes. An adapter is required only when you enable server-side rendering or on-demand routes — it teaches Astro how to build for a specific runtime (Node, Vercel, Netlify, Cloudflare). You set it via the dedicated adapter field, not the integrations array.

// astro.config.mjs — SSR with the Node adapter
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

export default defineConfig({
  output: 'server',
  adapter: node({ mode: 'standalone' }),
});
ConcernIntegrationAdapter
PurposeAdd features (UI frameworks, tooling)Target an SSR/edge runtime
Config fieldintegrations: []adapter: ...
Required for static sitesNoNo
Required for output: 'server'NoYes
ExamplesReact, Vue, Tailwind, SitemapNode, Vercel, Netlify, Cloudflare

In Astro 5 you can also mark individual pages as on-demand with export const prerender = false;, which still requires an adapter to be configured.

Using a UI framework island

Once a renderer integration is installed, you import framework components into .astro files and hydrate them selectively with client:* directives. Everything else stays static HTML.

---
// src/pages/index.astro
import Counter from '../components/Counter.jsx';
---
<h1>Static heading — zero JS</h1>
<Counter client:visible />

The Counter ships no JavaScript until it scrolls into view, while the heading is plain HTML. This is the islands architecture that makes Astro fast by default.

Official vs community integrations

Anything under the @astrojs/* scope is maintained by the Astro core team (renderers, adapters, sitemap, partytown, mdx). The community publishes hundreds more on the Astro Integrations directory — they install the same way, but check maintenance status and Astro version compatibility before relying on one in production.

Best practices

  • Prefer astro add over manual edits to avoid config drift and import mistakes.
  • Only add a renderer integration for frameworks you actually use — each one adds a Vite plugin and build cost.
  • Add an adapter only when you set output: 'server' or use on-demand pages; static sites need none.
  • Keep hydration minimal: reach for client:visible or client:idle before client:load.
  • Pin integration versions and verify Astro version compatibility before upgrading.
  • Review the diff astro add produces and commit it separately so config changes are easy to audit.
Last updated June 14, 2026
Was this helpful?