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 addfrom 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' }),
});
| Concern | Integration | Adapter |
|---|---|---|
| Purpose | Add features (UI frameworks, tooling) | Target an SSR/edge runtime |
| Config field | integrations: [] | adapter: ... |
| Required for static sites | No | No |
Required for output: 'server' | No | Yes |
| Examples | React, Vue, Tailwind, Sitemap | Node, 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 addover 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:visibleorclient:idlebeforeclient:load. - Pin integration versions and verify Astro version compatibility before upgrading.
- Review the diff
astro addproduces and commit it separately so config changes are easy to audit.