-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Sarah Rainsberger <[email protected]>
- Loading branch information
1 parent
70e6299
commit 8a09935
Showing
3 changed files
with
325 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
--- | ||
title: Astro Container API (experimental) | ||
i18nReady: false | ||
--- | ||
import Since from '~/components/Since.astro' | ||
|
||
<p><Since v="4.9.0" /></p> | ||
|
||
The Container API allows you to render Astro components in isolation. | ||
|
||
This experimental server-side API unlocks a variety of potential future uses, but is currently scoped to allow [unit testing of `.astro` component output](/en/guides/testing/#vitest-and-container-api). This API allows you to create a new container, and render an Astro component returning a string or a `Response`. | ||
|
||
This API is experimental and subject to breaking changes, even in [minor or patch releases](/en/upgrade-astro/#semantic-versioning). Please consult [the Astro CHANGELOG](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md) for changes as they occur. This page will always be updated with the most current information for the latest version of Astro. | ||
|
||
## `create()` | ||
|
||
Creates a new instance of the container. | ||
|
||
```js | ||
import { experimental_AstroContainer } from "astro/container"; | ||
|
||
const container = await experimental_AstroContainer.create(); | ||
``` | ||
|
||
It accepts an object with the following options: | ||
|
||
|
||
```ts | ||
export type AstroContainerOptions = { | ||
streaming?: boolean; | ||
renderers?: AstroRenderer[]; | ||
}; | ||
``` | ||
|
||
#### `streaming` option | ||
|
||
**Type:** `boolean` | ||
|
||
Enables rendering components using [HTML streaming](/en/guides/server-side-rendering/#html-streaming). | ||
|
||
#### `renderers` option | ||
|
||
**Type:**: `AstroRenderer[]`; | ||
|
||
A list of client renderers required by the component. Use this if your `.astro` component renders any [UI framework components](/en/guides/framework-components/) using an official Astro integration (e.g. React, Vue, etc.). For each framework rendered, you must provide an object stating the integration package `name`, as well as both its `client` and `server` rendering script | ||
|
||
The following example provides the necessary object to render an Astro component that renders a React component: | ||
|
||
```js | ||
const container = await experimental_AstroContainer.create({ | ||
renderers: [ | ||
{ | ||
name: "@astrojs/react", | ||
client: "@astrojs/react/client.js", | ||
server: "@astrojs/react/server.js" | ||
} | ||
] | ||
}) | ||
const result = await container.renderToString(ReactWrapper); | ||
``` | ||
|
||
## `renderToString()` | ||
|
||
This function renders a specified component inside a container. It takes an Astro component as an argument and it returns a string that represents the HTML/content rendered by the Astro component. | ||
|
||
```js | ||
import { experimental_AstroContainer } from "astro/container"; | ||
import Card from "../src/components/Card.astro"; | ||
|
||
const container = await experimental_AstroContainer.create(); | ||
const result = await container.renderToString(Card); | ||
``` | ||
|
||
Under the hood, this function calls [`renderToResponse`](#rendertoresponse) and calls `Response.text()`. | ||
|
||
It also accepts an object as a second argument that can contain a [number of options](#rendering-options). | ||
|
||
## `renderToResponse()` | ||
|
||
It renders a component, and it returns a `Response` object. | ||
|
||
```js | ||
import { experimental_AstroContainer } from "astro/container"; | ||
import Card from "../src/components/Card.astro"; | ||
|
||
const container = await experimental_AstroContainer.create(); | ||
const result = await container.renderToResponse(Card); | ||
``` | ||
|
||
It also accepts an object as a second argument that can contain a [number of options](#rendering-options). | ||
|
||
### Rendering options | ||
|
||
Both [`renderToResponse`](#rendertoresponse) and [`renderToString`](#rendertostring) accept an object as their second argument: | ||
|
||
```ts | ||
export type ContainerRenderOptions = { | ||
slots?: Record<string, any>; | ||
request?: Request; | ||
params?: Record<string, string | undefined>; | ||
locals?: App.Locals; | ||
routeType?: "page" | "endpoint"; | ||
|
||
}; | ||
``` | ||
|
||
These optional values can be passed to the rendering function in order to provide additional information necessary for an Astro component to properly render. | ||
|
||
#### `slots` | ||
|
||
**Type**: `Record<string, any>`; | ||
|
||
An option to pass content to be rendered with [`<slots>`](en/basics/astro-components/#slots). | ||
|
||
If your Astro component renders one default slot, pass an object with `default` as the key: | ||
|
||
```js name="Card.test.js" | ||
import Card from "../src/components/Card.astro"; | ||
|
||
const result = await container.renderToString(Card, { | ||
slots: { default: "Some value"} | ||
}); | ||
``` | ||
|
||
If your component renders named slots, use the slot names as the object keys: | ||
|
||
```astro name="Card.astro" | ||
--- | ||
--- | ||
<div> | ||
<slot name="header" /> | ||
<slot name="footer" /> | ||
</div> | ||
``` | ||
|
||
```js name="Card.test.js" | ||
import Card from "../src/components/Card.astro"; | ||
|
||
const result = await container.renderToString(Card, { | ||
slots: { "header": "Header content", "footer": "Footer" } | ||
}); | ||
``` | ||
|
||
You can also render components in cascade: | ||
|
||
```astro name="Card.astro" | ||
--- | ||
--- | ||
<div> | ||
<slot name="header" /> | ||
<slot name="footer" /> | ||
</div> | ||
``` | ||
|
||
```js name="Card.test.js" | ||
import Card from "../src/components/Card.astro"; | ||
import CardHeader from "../src/components/CardHeader.astro"; | ||
import CardFooter from "../src/components/CardFooter.astro"; | ||
|
||
const result = await container.renderToString(Card, { | ||
slots: { | ||
"header": await container.renderToString(CardHeader), | ||
"footer": await container.renderToString(CardFooter), | ||
} | ||
}); | ||
``` | ||
|
||
#### `request` option | ||
|
||
**Type**: `Request` | ||
|
||
An option to pass a `Request` with information about the path/URL the component will render. | ||
|
||
Use this option when your component needs to read information like `Astro.url` or `Astro.request`. | ||
|
||
You can also inject possible headers or cookies. | ||
|
||
```js file="Card.test.js" | ||
import Card from "../src/components/Card.astro"; | ||
|
||
const result = await container.renderToString(Card, { | ||
request: new Request("https://example.com/blog", { | ||
headers: { | ||
"X-some-secret-header": "test-value" | ||
} | ||
}) | ||
}); | ||
``` | ||
|
||
#### `params` option | ||
|
||
**Type**: `Record<string, string | undefined>`; | ||
|
||
An object to pass information about the path parameter to an Astro component responsible for [generating dynamic routes](/en/guides/routing/#dynamic-routes). | ||
|
||
Use this option when your component needs a value for `Astro.params` in order to generate a single route dynamically. | ||
|
||
```astro name="Card.astro" | ||
--- | ||
const { locale, slug } = Astro.params; | ||
--- | ||
<div></div> | ||
``` | ||
|
||
```js file="LocaleSlug.test.js" | ||
import LocaleSlug from "../src/components/[locale]/[slug].astro"; | ||
|
||
const result = await container.renderToString(LocaleSlug, { | ||
params: { | ||
locale: "en", | ||
slug: "getting-started" | ||
} | ||
}); | ||
``` | ||
|
||
#### `locals` options | ||
|
||
**Type**: `App.Locals` | ||
|
||
An option to pass information from [`Astro.locals`](/en/reference/api-reference/#astrolocals) for rendering your component. | ||
|
||
Use this option to when your component needs information stored during the lifecycle of a request in order to render, such as logged in status. | ||
|
||
```astro name="Card.astro" | ||
--- | ||
const { checkAuth } = Astro.locals; | ||
const isAuthenticated = checkAuth(); | ||
--- | ||
{isAuthenticated ? <span>You're in</span> : <span>You're out</span> } | ||
``` | ||
|
||
```js file="Card.test.js" | ||
import Card from "../src/components/Card.astro"; | ||
|
||
test("User is in", async () => { | ||
const result = await container.renderToString(Card, { | ||
locals: { | ||
checkAuth() { return true } | ||
} | ||
}); | ||
|
||
// assert result contains "You're in" | ||
}) | ||
|
||
|
||
test("User is out", async () => { | ||
const result = await container.renderToString(Card, { | ||
locals: { | ||
checkAuth() { return false } | ||
} | ||
}); | ||
|
||
// assert result contains "You're out" | ||
}) | ||
``` | ||
|
||
#### `routeType` option | ||
|
||
**Type**: `"page" | "endpoint"` | ||
|
||
An option available when using `renderToResponse` to specify that you are rendering an [endpoint](/en/guides/endpoints/): | ||
|
||
```js | ||
container.renderToString(Endpoint, { routeType: "endpoint" }); | ||
``` | ||
|
||
```js file="endpoint.test.js" | ||
import * as Endpoint from "../src/pages/api/endpoint.js"; | ||
|
||
const response = await container.renderToResponse(Endpoint, { | ||
routeType: "endpoint" | ||
}); | ||
const json = await response.json(); | ||
``` | ||
|
||
To test your endpoint on methods such as `POST`, `PATCH`, etc., use the `request` option to call the correct function: | ||
|
||
```js file="endpoint.js" | ||
export function GET() {} | ||
|
||
// need to test this | ||
export function POST() {} | ||
``` | ||
|
||
```js file="endpoint.test.js" ins={5-7} | ||
import * as Endpoint from "../src/pages/api/endpoint.js"; | ||
|
||
const response = await container.renderToResponse(Endpoint, { | ||
routeType: "endpoint", | ||
request: new Request("https://example.com", { | ||
method: "POST" // | ||
}) | ||
}); | ||
const json = await response.json(); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters