Skip to main content
The Integration API allows you to extend Astro with custom functionality through lifecycle hooks. Build integrations to add UI framework support, inject middleware, add dev toolbar apps, and more.

When to use this API

Use the Integration API when you need to:
  • Add support for UI frameworks (React, Vue, Svelte, etc.)
  • Inject routes, scripts, or styles into every page
  • Add middleware to intercept requests
  • Modify the Vite or Astro configuration
  • Hook into the build process
  • Add custom dev toolbar apps
  • Extend TypeScript types in user projects

Creating an integration

An integration is an object with a name and hooks property:
export default function myIntegration() {
  return {
    name: 'my-integration',
    hooks: {
      'astro:config:setup': ({ updateConfig }) => {
        // Hook implementation
      }
    }
  };
}

Lifecycle hooks

Integrations execute during specific lifecycle events. All hooks receive a logger option for writing logs.

astro:config:setup

Runs on initialization before Vite or Astro config have resolved. Use this to extend the project configuration.
config
AstroConfig
Read-only copy of the user-supplied Astro config, resolved before other integrations run.
command
'dev' | 'build' | 'preview' | 'sync'
The command being executed:
  • dev - Running astro dev
  • build - Running astro build
  • preview - Running astro preview
  • sync - Running astro sync
isRestart
boolean
false when dev server starts, true when reload is triggered. Useful to detect when this function is called more than once.
updateConfig
(newConfig: DeepPartial<AstroConfig>) => AstroConfig
Callback function to update the Astro config. Config is merged with user config and other integration updates.
updateConfig({
  vite: {
    plugins: [myVitePlugin()],
  }
})
addRenderer
(renderer: AstroRenderer) => void
Adds a component framework renderer (React, Vue, Svelte, etc.).
addRenderer({
  name: 'my-renderer',
  serverEntrypoint: './server.js',
  clientEntrypoint: './client.js'
})
addWatchFile
(path: URL | string) => void
Adds a file to watch. When changed, the dev server will reload.
addWatchFile(new URL('./config.json', import.meta.url))
addClientDirective
(directive: ClientDirectiveConfig) => void
Adds a custom client directive for use in .astro files.
addClientDirective({
  name: 'click',
  entrypoint: './click-directive.js'
})
addMiddleware
(middleware: AstroIntegrationMiddleware) => void
Adds middleware to run on each request.
addMiddleware({
  entrypoint: '@my-package/middleware',
  order: 'pre' // or 'post'
})
addDevToolbarApp
(entrypoint: DevToolbarAppEntry) => void
Adds a custom dev toolbar app.
addDevToolbarApp({
  id: 'my-plugin',
  name: 'My Plugin',
  entrypoint: './toolbar-app.js'
})
injectScript
(stage: InjectedScriptStage, content: string) => void
Injects a JavaScript string onto every page.Stages:
  • head-inline - In <head>, not optimized by Vite
  • before-hydration - Client-side, before hydration, optimized by Vite
  • page - In <script type="module">, bundled by Vite
  • page-ssr - In frontmatter, runs once on import
injectScript('page-ssr', 'import "global-styles.css";')
injectRoute
(route: InjectedRoute) => void
Injects routes into the project.
injectRoute({
  pattern: '/admin',
  entrypoint: './admin-page.astro'
})
createCodegenDir
() => URL
Creates and returns the path to .astro/integrations/<integration-name>/.
const codegenDir = createCodegenDir()
writeFileSync(new URL('cache.json', codegenDir), '{}')

astro:config:done

Runs after Astro config has resolved and other integrations have run. Use this to read the final config.
config
AstroConfig
Final resolved Astro config after all integrations have run.
setAdapter
(adapter: AstroAdapter) => void
Makes the integration an adapter. See the Adapter API.
injectTypes
(injectedType: InjectedType) => URL
Injects TypeScript types into the user’s project.
injectTypes({
  filename: 'types.d.ts',
  content: 'declare module "virtual:my-integration" {}'
})
buildOutput
'static' | 'server'
The project’s build output type based on configuration.

astro:server:setup

Runs just after the Vite server is created in dev mode. Use this to add Vite middleware or enable content refresh.
server
ViteDevServer
Mutable Vite dev server instance.
server.middlewares.use((req, res, next) => {
  // Handle requests
})
toolbar
ToolbarServerHelpers
Object with callback functions to interact with the dev toolbar:
  • toolbar.on(event, callback) - Listen for toolbar events
  • toolbar.send(event, payload) - Send messages to toolbar
  • toolbar.onAppInitialized(appId, callback) - Run when app initializes
  • toolbar.onAppToggled(appId, callback) - Run when app toggles
refreshContent
(options?: RefreshContentOptions) => Promise<void>
Triggers an update to the content layer during dev.
await refreshContent({
  loaders: ['my-loader'],
  context: { webhookBody }
})

astro:server:start

Runs just after the server’s listen() event. Use this to intercept network requests.
address
AddressInfo
The address, family, and port number from Node’s server.address() method.

astro:server:done

Runs just after the dev server closes. Use this to run cleanup events.

astro:build:start

Runs after astro:config:done, before the production build begins. Use this to set up global objects or clients.

astro:build:setup

Runs immediately before the build. This is your final chance to modify the Vite config.
vite
InlineConfig
The Vite configuration used in the build.
pages
Map<string, PageBuildData>
Map of pages with their build data.
pages.forEach((data, route) => {
  if (data.route.pattern.test('/blog')) {
    console.log(data.route.type)
  }
})
target
'client' | 'server'
Whether this is the client or server build phase.
updateConfig
(newConfig: InlineConfig) => void
Updates the Vite config for the build.
updateConfig({
  plugins: [myVitePlugin()]
})

astro:build:ssr

Runs after a production SSR build completes. Use this to access the SSR manifest and entry points.
manifest
SerializedSSRManifest
Serialized SSR manifest with routing information.
entryPoints
Map<IntegrationRouteData, URL>
Map of emitted entry points with route data as key and file URL as value.
entryPoints.forEach((url, route) => {
  console.log(url.href)
})
middlewareEntryPoint
URL | undefined
The middleware file path if middleware exists.

astro:build:generated

Runs after static build has finished generating routes and assets. Use this to access generated files before cleanup.
dir
URL
URL path to the build output directory.

astro:build:done

Runs after production build (SSG or SSR) completes. Use this to access generated routes and assets.
dir
URL
URL path to the build output directory.
pages
{ pathname: string }[]
List of all generated pages with their finalized paths.
assets
Map<string, URL[]>
Map of output file paths grouped by route pattern.

TypeScript interface

interface AstroIntegration {
  name: string;
  hooks: {
    'astro:config:setup'?: (options: {
      config: AstroConfig;
      command: 'dev' | 'build' | 'preview' | 'sync';
      isRestart: boolean;
      updateConfig: (newConfig: DeepPartial<AstroConfig>) => AstroConfig;
      addRenderer: (renderer: AstroRenderer) => void;
      addWatchFile: (path: URL | string) => void;
      addClientDirective: (directive: ClientDirectiveConfig) => void;
      addMiddleware: (middleware: AstroIntegrationMiddleware) => void;
      addDevToolbarApp: (entrypoint: DevToolbarAppEntry) => void;
      injectScript: (stage: InjectedScriptStage, content: string) => void;
      injectRoute: (injectedRoute: InjectedRoute) => void;
      createCodegenDir: () => URL;
      logger: AstroIntegrationLogger;
    }) => void | Promise<void>;
    'astro:config:done'?: (options: {
      config: AstroConfig;
      setAdapter: (adapter: AstroAdapter) => void;
      injectTypes: (injectedType: InjectedType) => URL;
      logger: AstroIntegrationLogger;
      buildOutput: 'static' | 'server';
    }) => void | Promise<void>;
    'astro:server:setup'?: (options: {
      server: ViteDevServer;
      logger: AstroIntegrationLogger;
      toolbar: ToolbarServerHelpers;
      refreshContent?: (options: RefreshContentOptions) => Promise<void>;
    }) => void | Promise<void>;
    'astro:server:start'?: (options: {
      address: AddressInfo;
      logger: AstroIntegrationLogger;
    }) => void | Promise<void>;
    'astro:server:done'?: (options: {
      logger: AstroIntegrationLogger;
    }) => void | Promise<void>;
    'astro:build:start'?: (options: {
      logger: AstroIntegrationLogger;
    }) => void | Promise<void>;
    'astro:build:setup'?: (options: {
      vite: InlineConfig;
      pages: Map<string, PageBuildData>;
      target: 'client' | 'server';
      updateConfig: (newConfig: InlineConfig) => void;
      logger: AstroIntegrationLogger;
    }) => void | Promise<void>;
    'astro:build:ssr'?: (options: {
      manifest: SerializedSSRManifest;
      entryPoints: Map<IntegrationRouteData, URL>;
      middlewareEntryPoint: URL | undefined;
      logger: AstroIntegrationLogger;
    }) => void | Promise<void>;
    'astro:build:generated'?: (options: {
      dir: URL;
      logger: AstroIntegrationLogger;
    }) => void | Promise<void>;
    'astro:build:done'?: (options: {
      pages: { pathname: string }[];
      dir: URL;
      assets: Map<string, URL[]>;
      logger: AstroIntegrationLogger;
    }) => void | Promise<void>;
  };
}

Example integration

import type { AstroIntegration } from 'astro';

export default function myIntegration(): AstroIntegration {
  return {
    name: 'my-integration',
    hooks: {
      'astro:config:setup': ({ updateConfig, addMiddleware, logger }) => {
        logger.info('Setting up integration');
        
        // Add Vite plugin
        updateConfig({
          vite: {
            plugins: [myVitePlugin()]
          }
        });
        
        // Add middleware
        addMiddleware({
          entrypoint: './middleware.js',
          order: 'pre'
        });
      },
      
      'astro:config:done': ({ config, injectTypes }) => {
        // Inject types
        injectTypes({
          filename: 'types.d.ts',
          content: 'declare module "virtual:my-integration" {}'
        });
      },
      
      'astro:build:done': ({ dir, pages, logger }) => {
        logger.info(`Built ${pages.length} pages to ${dir}`);
      }
    }
  };
}