Container Reference
Purpose and Scope
The Astro Container API is an experimental server-side rendering surface for rendering Astro components outside the normal route request lifecycle. Its primary reader task is testing component output in Vite environments such as Vitest, but it also supports programmatic rendering in shell environments where Astro is not controlling the entire HTTP server. The key mental model is that a container creates enough of Astro’s rendering pipeline to execute a component, endpoint, middleware, slots, params, locals, props, and renderer dependencies without starting a full application route. This page documents that boundary and the source modules that implement it.
Sources: packages/astro/src/container/index.ts, packages/astro/src/container/pipeline.ts
Because the word container appears in two places, distinguish the public experimental Container API from Astro’s internal development server container. The public API is exposed through the container package and is shaped around rendering components or endpoints. The internal development container in core dev code owns the Vite dev server, file system, logger, inline config, restart state, request handler, and close lifecycle. Both participate in development-time rendering, but they solve different problems: one is a user-facing rendering harness, and the other is the process-level server wrapper used by astro dev.
Sources: packages/astro/src/container/index.ts, packages/astro/src/core/dev/container.ts
Relevant Source Files
- packages/astro/src/container/index.ts — Defines the public container-facing types, including render options for slots, requests, params, locals, route type, props, and partial rendering, plus renderer-related types used by container creation.
- packages/astro/src/container/pipeline.ts — Implements ContainerPipeline, a Pipeline subclass that supplies head elements, rewrite handling, route insertion, and component lookup for container rendering.
- packages/astro/src/container/vite-plugin-container.ts — Provides the Vite plugin that resolves the astro:container virtual module to Astro’s virtual module implementation.
- packages/astro/src/virtual-modules/container.ts — Exports loadRenderers(), the helper that imports renderer server entrypoints and returns loaded SSR renderer objects for container use in Vite or Vitest.
- packages/astro/src/vite-plugin-integrations-container/index.ts — Connects Astro integrations into Vite for container scenarios and resolves injected route entrypoints using the SSR plugin container in dev.
- packages/astro/src/core/dev/container.ts — Defines and creates Astro’s internal dev server container, including integration hooks, Vite server creation, route list creation, renderer client dependency optimization, and sync behavior.
Public API Components
The public entry point described by Astro’s docs is imported from astro/container as experimental_AstroContainer. The official signature for creation is create(options?: AstroContainerOptions), returning a promise for a container instance. The repository source grounds the option vocabulary used by that API: container rendering can accept slots, a Request, dynamic route params, App.Locals, a route type such as endpoint, Astro props, and a partial flag. By default the API is oriented toward partial component rendering, while partial: false forces behavior closer to a full page render when the caller needs page-level output.
Sources: packages/astro/src/container/index.ts
The render options make the container useful for realistic tests instead of only static string snapshots. A component that reads Astro.url or Astro.request can be rendered with a Request. A dynamic route component can receive params, and code that normally depends on middleware-populated locals can receive locals directly. Endpoint rendering is represented by routeType, which lets the same harness exercise route handlers rather than only .astro component factories. Slots and props let tests construct the same inputs that page composition would provide in an application, including default and named slot content.
Sources: packages/astro/src/container/index.ts
Compact reference
| Name | Source-level contract | When to use it |
|---|---|---|
| experimental_AstroContainer.create(options) | Official public factory returning a container instance | Start an isolated render harness for tests or programmatic rendering |
| AstroContainerOptions.streaming | Boolean option described by official docs | Enable HTML streaming behavior when rendering through the container |
| AstroContainerOptions.renderers | Array of server renderers | Render framework components or MDX that require an Astro renderer |
| ContainerRenderOptions.slots | Record keyed by slot name | Provide default or named slot content |
| ContainerRenderOptions.request | Request | Supply URL and request data for Astro.request and Astro.url dependent code |
| ContainerRenderOptions.params | Record of route parameter names to strings or undefined | Exercise dynamic route behavior |
| ContainerRenderOptions.locals | App.Locals | Inject values normally supplied by middleware |
| ContainerRenderOptions.routeType | RouteType | Render endpoints as endpoint routes |
| ContainerRenderOptions.props | Props | Populate Astro.props for component rendering |
| ContainerRenderOptions.partial | Boolean | Control whether rendering behaves as a partial or a full page |
| loadRenderers(renderers) | Imports renderer.serverEntrypoint and attaches the default export as ssr | Prepare official or custom renderers for Vite and Vitest container use |
Renderer Loading and the astro:container Virtual Module
Astro components are native to Astro, but framework components require renderers. The virtual module astro:container exists to support that workflow in Vite-powered environments. The Vite plugin named astro:container intercepts the exact virtual module identifier and resolves it to astro/virtual-modules/container.js. That implementation exports loadRenderers(), which accepts AstroRenderer definitions, dynamically imports each renderer server entrypoint, and returns only successfully loaded SSR renderer objects. This keeps tests close to normal Astro behavior while avoiding the need for callers to hand-wire every renderer implementation detail.
Sources: packages/astro/src/container/vite-plugin-container.ts, packages/astro/src/virtual-modules/container.ts
A typical Vitest setup for a component that uses React, Vue, Svelte, Solid, MDX, or another renderer follows a two-step pattern. First, import getContainerRenderer from the integration-specific container renderer package and pass it to loadRenderers(). Second, pass the loaded renderers into create(). Local Astro-only components do not need that extra renderer list, but mixed component trees do because the server renderer is responsible for checking and rendering framework component syntaxes that Astro does not handle natively. This mirrors the renderer concept used by integrations, where server entrypoints render to HTML and optional client entrypoints support hydration.
Sources: packages/astro/src/virtual-modules/container.ts
import { getContainerRenderer } from '@astrojs/react/container-renderer';
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import { loadRenderers } from 'astro:container';
const renderers = await loadRenderers([getContainerRenderer]);
const container = await AstroContainer.create({ renderers });Pipeline and Rendering Behavior
ContainerPipeline is the implementation bridge between the public rendering harness and Astro’s core render pipeline. It extends the base Pipeline and identifies itself as ContainerPipeline. Its factory accepts the logger, manifest, renderers, resolve function, and streaming flag, then creates a development-mode pipeline. That matters because component rendering still needs route metadata, head elements, styles, scripts, rewrite behavior, and component lookup. The container is therefore not a separate renderer; it is a controlled entry point into Astro’s established SSR machinery with a smaller manifest and caller-provided route context.
Sources: packages/astro/src/container/pipeline.ts
Head management is one example of that reuse. ContainerPipeline finds the route information that matches the RouteData being rendered, builds stylesheet elements from route styles, and adds module scripts or inline head scripts depending on script shape. Rewrites are also pipeline-aware: tryRewrite delegates route matching to the core rewrite helper using manifest routes, trailing slash behavior, build format, base, and output directory. Once a rewrite target is found, the pipeline loads the matching component instance and returns the route data, new URL, and pathname needed to continue rendering.
Sources: packages/astro/src/container/pipeline.ts
The pipeline stores component instances in a WeakMap keyed by RouteData. insertRoute associates a route with a component instance and the resolved middleware, while getComponentByRoute resolves the stored page module for a route. If a requested route was not inserted, the pipeline throws an error identifying the missing pathname. This implementation detail explains why programmatic rendering needs a route-shaped context even when the caller thinks in terms of one component: Astro’s SSR stack is route-aware, and the container supplies a narrow route manifest around the component under test.
Sources: packages/astro/src/container/pipeline.ts
Integration and Dev-Server Flow
The integration container plugin connects Astro integrations into Vite when container rendering participates in development or build workflows. During configureServer it saves the Vite server and, unless the server is production, runs the astro:server:setup hook with the resolved config, Vite server, and logger. During buildStart it resolves injected route entrypoints only when injected routes have not already been resolved. In dev it uses the SSR environment’s plugin container to resolve entrypoints, deliberately avoiding a client dependency optimizer race; in build it falls back to the plugin context resolver.
Sources: packages/astro/src/vite-plugin-integrations-container/index.ts
Astro’s internal dev container shows the wider lifecycle that surrounds Vite-based rendering. createContainer runs config setup hooks for the dev command, computes the open URL behavior from server config and base, pre-optimizes renderer client entrypoints, sets the build output default before config done hooks, creates the route manifest list, builds the Vite configuration, starts a Vite dev server, and runs internal sync with content and cleanup controls. These details are not all public Container API options, but they explain why container rendering in Vite can cooperate with integrations, routes, renderers, and generated state.
Sources: packages/astro/src/core/dev/container.ts
Practical Usage Patterns
For unit tests, start with the smallest container that can render the component under test. If the component is pure Astro markup, create a container and call the render method with props or slots. If it reads request data, construct a Request with the URL you want the component to observe. If it depends on locals, pass locals directly rather than reproducing the entire middleware chain. If it is an endpoint, set routeType to endpoint so the rendering harness treats it as route-handler output. This lets tests describe the runtime contract without booting a full dev server.
Sources: packages/astro/src/container/index.ts
For framework islands or MDX, load renderers before creating the container. Treat renderers as part of the test fixture: they are required whenever a component tree contains syntax that Astro itself does not render. For on-demand or shell environments outside Vite, the same conceptual requirement applies, but renderer loading may need to be done manually rather than through the astro:container virtual module. Because the API is experimental, keep helpers centralized in your test utilities so changes to creation options, renderer loading, or rendering return types can be updated in one place.
Sources: packages/astro/src/virtual-modules/container.ts, packages/astro/src/container/index.ts
Testing Signals and Next Steps
The strongest testing signal in the source is that the API exposes request, params, locals, slots, props, endpoint route type, partial behavior, streaming, and renderers as explicit inputs. Prefer assertions that exercise those inputs over brittle implementation snapshots. For example, test that a dynamic route renders the expected path-derived output, that locals-dependent components receive the value you supplied, or that a named slot appears in the correct place. When testing integration-heavy components, verify that loadRenderers() is part of setup so framework components are rendered through their server entrypoints.
Sources: packages/astro/src/container/index.ts, packages/astro/src/virtual-modules/container.ts
Next, read the API Reference for the broader astro package exports, the Astro Modules Reference for virtual modules such as astro:container, and the Server-Side Rendering and Adapters page if your programmatic rendering use case overlaps with on-demand rendering. If you are writing an integration or renderer, pair this page with the Renderer Reference concepts: a renderer is an integration-provided server module, optionally paired with a client module, and container rendering needs the loaded server side to render framework component output correctly.