Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Astro's integration system provides extensibility through hooks that allow integrations to modify configuration, inject routes, add renderers, and extend the build process.
Defines an Astro integration.
interface AstroIntegration {
/**
* Integration name
*/
name: string;
/**
* Integration hooks
*/
hooks: {
'astro:config:setup'?: (
options: HookParameters<'astro:config:setup'>
) => void | Promise<void>;
'astro:config:done'?: (
options: HookParameters<'astro:config:done'>
) => void | Promise<void>;
'astro:server:setup'?: (
options: HookParameters<'astro:server:setup'>
) => void | Promise<void>;
'astro:server:start'?: (
options: HookParameters<'astro:server:start'>
) => void | Promise<void>;
'astro:server:done'?: (
options: HookParameters<'astro:server:done'>
) => void | Promise<void>;
'astro:build:start'?: (
options: HookParameters<'astro:build:start'>
) => void | Promise<void>;
'astro:build:setup'?: (
options: HookParameters<'astro:build:setup'>
) => void | Promise<void>;
'astro:build:generated'?: (
options: HookParameters<'astro:build:generated'>
) => void | Promise<void>;
'astro:build:ssr'?: (
options: HookParameters<'astro:build:ssr'>
) => void | Promise<void>;
'astro:build:done'?: (
options: HookParameters<'astro:build:done'>
) => void | Promise<void>;
'astro:route:setup'?: (
options: HookParameters<'astro:route:setup'>
) => void | Promise<void>;
'astro:routes:resolved'?: (
options: HookParameters<'astro:routes:resolved'>
) => void | Promise<void>;
};
}Runs during initial configuration setup. Most powerful hook for modifying configuration.
interface ConfigSetupHookParameters {
/**
* Current Astro configuration
*/
config: AstroConfig;
/**
* Update Astro configuration
* Returns the updated configuration
*/
updateConfig: (newConfig: DeepPartial<AstroConfig>) => AstroConfig;
/**
* Add renderer for framework components
*/
addRenderer: (renderer: AstroRenderer) => void;
/**
* Add client directive (e.g., client:visible)
*/
addClientDirective: (directive: ClientDirectiveConfig) => void;
/**
* Add dev toolbar app
*/
addDevToolbarApp: (entrypoint: string) => void;
/**
* Add middleware
*/
addMiddleware: (middleware: AddMiddleware) => void;
/**
* Add watch files for dev server
*/
addWatchFile: (file: URL | string) => void;
/**
* Inject route into the project
*/
injectRoute: (route: InjectedRoute) => void;
/**
* Inject script into pages
*/
injectScript: (stage: InjectedScriptStage, content: string) => void;
/**
* Create a directory for generated files
* Returns URL to the created directory
*/
createCodegenDir: () => URL;
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
/**
* Command being run
*/
command: 'dev' | 'build' | 'preview' | 'sync';
/**
* Build mode
*/
isRestart: boolean;
}export default function myIntegration(): AstroIntegration {
return {
name: 'my-integration',
hooks: {
'astro:config:setup': ({
config,
updateConfig,
addRenderer,
injectRoute,
logger,
}) => {
logger.info('Setting up integration');
// Update configuration
updateConfig({
vite: {
optimizeDeps: {
include: ['my-package'],
},
},
});
// Add a renderer
addRenderer({
name: 'my-renderer',
serverEntrypoint: 'my-renderer/server.js',
});
// Inject a route
injectRoute({
pattern: '/api/my-endpoint',
entrypoint: './api/endpoint.ts',
});
},
},
};
}Runs after configuration is finalized. Read-only access to config.
interface ConfigDoneHookParameters {
/**
* Final Astro configuration (read-only)
*/
config: AstroConfig;
/**
* Set adapter for SSR
*/
setAdapter: (adapter: AstroAdapter) => void;
/**
* Inject TypeScript type definitions
* Returns URL to the generated type file
*/
injectTypes: (injectedType: InjectedType) => URL;
/**
* Build output mode
*/
buildOutput: 'static' | 'server';
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
}
interface InjectedType {
/**
* Filename for the type definition file
*/
filename: string;
/**
* TypeScript content to inject
*/
content: string;
}Runs when dev server is created.
interface ServerSetupHookParameters {
/**
* Vite dev server instance
*/
server: ViteDevServer;
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
/**
* Toolbar tools
*/
toolbar: ToolbarServerHelpers;
/**
* Refresh content collections (optional)
*/
refreshContent?: (options: RefreshContentOptions) => Promise<void>;
}
interface RefreshContentOptions {
/**
* Loaders to refresh (optional)
*/
loaders?: string[];
/**
* Context information (optional)
*/
context?: Record<string, unknown>;
}Runs after dev server starts.
interface ServerStartHookParameters {
/**
* Server address
*/
address: AddressInfo;
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
}Runs before build starts.
interface BuildStartHookParameters {
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
}Runs during build setup.
interface BuildSetupHookParameters {
/**
* Vite build configuration
*/
vite: ViteInlineConfig;
/**
* Pages to build
*/
pages: Map<string, PageBuildData>;
/**
* Target output ('client' or 'server')
*/
target: 'client' | 'server';
/**
* Update Vite configuration
*/
updateConfig: (config: ViteInlineConfig) => void;
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
}Runs after static build generates files.
interface BuildGeneratedHookParameters {
/**
* Build output directory
*/
dir: URL;
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
}Runs after SSR build completes.
interface BuildSSRHookParameters {
/**
* SSR manifest
*/
manifest: SSRManifest;
/**
* Entry points map
*/
entryPoints: Map<RouteData, URL>;
/**
* Middleware entry point
*/
middlewareEntryPoint: URL | undefined;
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
}Runs after entire build completes.
interface BuildDoneHookParameters {
/**
* Build output directory
*/
dir: URL;
/**
* Generated routes
* @deprecated Use the `assets` map and the new `astro:routes:resolved` hook
*/
routes: IntegrationRouteData[];
/**
* Generated pages
*/
pages: { pathname: string }[];
/**
* Assets map from routes to their generated asset URLs
*/
assets: Map<string, URL[]>;
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
}Runs when a route is being set up during the build process. This hook is called for each route being processed.
interface RouteSetupHookParameters {
/**
* Route options including component path and prerender settings
*/
route: RouteOptions;
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
}
interface RouteOptions {
/**
* The path to this route relative to the project root. The slash is normalized as forward slash
* across all OS.
* @example "src/pages/blog/[...slug].astro"
*/
readonly component: string;
/**
* Whether this route should be prerendered. If the route has an explicit `prerender` export,
* the value will be passed here. Otherwise, it's undefined and will fallback to a prerender
* default depending on the `output` option.
*/
prerender?: boolean;
}Runs after all routes have been resolved during the build process. This hook provides complete information about all routes in the project.
interface RoutesResolvedHookParameters {
/**
* Resolved routes with complete metadata
*/
routes: IntegrationResolvedRoute[];
/**
* Integration logger
*/
logger: AstroIntegrationLogger;
}
interface IntegrationResolvedRoute {
/**
* Route pattern (e.g., "/blog/[slug]")
*/
pattern: string;
/**
* Regular expression for matching the route pattern
*/
patternRegex: RegExp;
/**
* Path to the component file
*/
entrypoint: string;
/**
* Whether this route is prerendered
*/
isPrerendered: boolean;
/**
* Function to generate the route path from params
*/
generate: (data?: any) => string;
/**
* Dynamic and spread route params
*/
params: string[];
/**
* Output URL pathname (undefined for dynamic/spread routes)
*/
pathname?: string;
/**
* Route segments
*/
segments: RoutePart[][];
/**
* Route type
*/
type: 'page' | 'endpoint' | 'redirect' | 'fallback';
/**
* Redirect configuration if this is a redirect route
*/
redirect?: RedirectConfig;
/**
* Route origin (user-defined or internal)
*/
origin?: 'user' | 'internal';
/**
* Redirect route information if this route redirects to another
*/
redirectRoute?: IntegrationResolvedRoute;
}
interface RoutePart {
/**
* Segment content
*/
content: string;
/**
* Whether this segment is dynamic (e.g., [id])
*/
dynamic: boolean;
/**
* Whether this segment is a spread parameter (e.g., [...slug])
*/
spread: boolean;
}Defines a framework renderer (React, Vue, Svelte, etc.).
interface AstroRenderer {
/**
* Renderer name
*/
name: string;
/**
* Server entrypoint for SSR
*/
serverEntrypoint: string;
/**
* Client entrypoint for hydration
*/
clientEntrypoint?: string;
/**
* JSX import source
*/
jsxImportSource?: string;
/**
* JSX transformation plugin
*/
jsxTransformOptions?: (options: {
mode: string;
ssr: boolean;
}) => Promise<{ jsx: 'react' | 'preserve'; plugins?: any[] }>;
}Example Renderer:
export default function reactIntegration(): AstroIntegration {
return {
name: '@astrojs/react',
hooks: {
'astro:config:setup': ({ addRenderer }) => {
addRenderer({
name: '@astrojs/react',
serverEntrypoint: '@astrojs/react/server.js',
clientEntrypoint: '@astrojs/react/client.js',
jsxImportSource: 'react',
});
},
},
};
}Custom client: directives for hydration strategies.
interface ClientDirectiveConfig {
/**
* Directive name (used as client:name)
*/
name: string;
/**
* Entrypoint module
*/
entrypoint: string;
}export default function customHydration(): AstroIntegration {
return {
name: 'custom-hydration',
hooks: {
'astro:config:setup': ({ addClientDirective }) => {
addClientDirective({
name: 'custom',
entrypoint: './client-directive.js',
});
},
},
};
}Add routes programmatically.
interface InjectedRoute {
/**
* Route pattern (e.g., '/api/data')
*/
pattern: string;
/**
* Route entrypoint file
*/
entrypoint: string;
/**
* Prerender the route
* @default false
*/
prerender?: boolean;
}export default function apiRoutes(): AstroIntegration {
return {
name: 'api-routes',
hooks: {
'astro:config:setup': ({ injectRoute }) => {
injectRoute({
pattern: '/api/health',
entrypoint: './src/api/health.ts',
});
injectRoute({
pattern: '/sitemap.xml',
entrypoint: './src/api/sitemap.ts',
prerender: true,
});
},
},
};
}Logging utilities for integrations.
interface AstroIntegrationLogger {
/**
* Log info message
*/
info(message: string): void;
/**
* Log warning message
*/
warn(message: string): void;
/**
* Log error message
*/
error(message: string): void;
/**
* Log debug message
*/
debug(message: string): void;
/**
* Create child logger with prefix
*/
fork(label: string): AstroIntegrationLogger;
}export default function simpleIntegration(): AstroIntegration {
return {
name: 'simple-integration',
hooks: {
'astro:config:setup': ({ logger }) => {
logger.info('Setting up simple integration');
},
'astro:config:done': ({ config, logger }) => {
logger.info(`Site: ${config.site}`);
},
},
};
}export default function vitePluginIntegration(): AstroIntegration {
return {
name: 'vite-plugin-integration',
hooks: {
'astro:config:setup': ({ updateConfig }) => {
updateConfig({
vite: {
plugins: [myVitePlugin()],
},
});
},
},
};
}export default function routeInjection(): AstroIntegration {
return {
name: 'route-injection',
hooks: {
'astro:config:setup': ({ injectRoute, addWatchFile }) => {
// Inject API routes
injectRoute({
pattern: '/api/posts',
entrypoint: './src/api/posts.ts',
});
// Watch external file
addWatchFile(new URL('./data/posts.json', import.meta.url));
},
},
};
}import fs from 'fs/promises';
import path from 'path';
export default function postProcess(): AstroIntegration {
return {
name: 'post-process',
hooks: {
'astro:build:done': async ({ dir, pages, logger }) => {
logger.info(`Processing ${pages.length} pages`);
for (const page of pages) {
const filePath = path.join(dir.pathname, page.pathname, 'index.html');
let content = await fs.readFile(filePath, 'utf-8');
// Modify content
content = content.replace('</body>', '<script>console.log("Injected")</script></body>');
await fs.writeFile(filePath, content);
}
logger.info('Post-processing complete');
},
},
};
}export default function customAdapter(): AstroIntegration {
return {
name: 'custom-adapter',
hooks: {
'astro:config:setup': ({ updateConfig }) => {
updateConfig({
vite: {
ssr: {
noExternal: ['my-platform-package'],
},
},
});
},
'astro:config:done': ({ setAdapter, config }) => {
setAdapter({
name: 'custom-adapter',
serverEntrypoint: 'custom-adapter/server.js',
supportedAstroFeatures: {
staticOutput: 'stable',
serverOutput: 'stable',
hybridOutput: 'stable',
},
});
},
'astro:build:done': async ({ dir, logger }) => {
logger.info('Preparing deployment package');
// Copy files, create manifests, etc.
},
},
};
}export default function devToolbar(): AstroIntegration {
return {
name: 'dev-toolbar',
hooks: {
'astro:config:setup': ({ addDevToolbarApp }) => {
addDevToolbarApp('./toolbar-app.ts');
},
},
};
}interface AddMiddleware {
/**
* Middleware entrypoint
*/
entrypoint: string;
/**
* Middleware order
* - 'pre': Run before user middleware
* - 'post': Run after user middleware
* @default 'pre'
*/
order?: 'pre' | 'post';
}type InjectedScriptStage =
| 'head-inline'
| 'before-hydration'
| 'page'
| 'page-ssr';'astro:config:setup': ({ injectScript }) => {
// Inline script in <head>
injectScript('head-inline', 'console.log("Init")');
// Script before hydration
injectScript('before-hydration', 'import "./setup.js"');
}