diff --git a/.changeset/thirty-dots-end.md b/.changeset/thirty-dots-end.md new file mode 100644 index 000000000000..399d0dc81442 --- /dev/null +++ b/.changeset/thirty-dots-end.md @@ -0,0 +1,17 @@ +--- +"astro": patch +--- + +You can now pass `props` when rendering a component using the Container APIs: + +```js +import { experimental_AstroContainer as AstroContainer } from "astro/contaienr"; +import Card from "../src/components/Card.astro"; + +const container = await AstroContainer.create(); +const result = await container.renderToString(Card, { + props: { + someState: true + } +}); +``` diff --git a/packages/astro/src/container/index.ts b/packages/astro/src/container/index.ts index 82859799be22..824bc96a4ce8 100644 --- a/packages/astro/src/container/index.ts +++ b/packages/astro/src/container/index.ts @@ -3,7 +3,7 @@ import type { AstroRenderer, AstroUserConfig, ComponentInstance, - MiddlewareHandler, + MiddlewareHandler, Props, RouteData, RouteType, SSRLoadedRenderer, @@ -70,6 +70,15 @@ export type ContainerRenderOptions = { * ``` */ routeType?: RouteType; + + /** + * Allows to pass `Astro.props` to an Astro component: + * + * ```js + * container.renderToString(Endpoint, { props: { "lorem": "ipsum" } }); + * ``` + */ + props?: Props }; function createManifest( @@ -369,6 +378,9 @@ export class experimental_AstroContainer { if (options.params) { renderContext.params = options.params; } + if (options.props) { + renderContext.props = options.props; + } return renderContext.render(componentInstance, slots); } diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts index 27eebc632060..307a3f240054 100644 --- a/packages/astro/src/core/render-context.ts +++ b/packages/astro/src/core/render-context.ts @@ -3,7 +3,7 @@ import type { AstroGlobal, AstroGlobalPartial, ComponentInstance, - MiddlewareHandler, + MiddlewareHandler, Props, RewritePayload, RouteData, SSRResult, @@ -47,7 +47,8 @@ export class RenderContext { public status: number, protected cookies = new AstroCookies(request), public params = getParams(routeData, pathname), - protected url = new URL(request.url) + protected url = new URL(request.url), + public props: Props = {} ) {} /** @@ -97,7 +98,7 @@ export class RenderContext { ): Promise { const { cookies, middleware, pathname, pipeline } = this; const { logger, routeCache, serverLike, streaming } = pipeline; - const props = await getProps({ + const props = Object.keys(this.props).length > 0 ? this.props : await getProps({ mod: componentInstance, routeData: this.routeData, routeCache, diff --git a/packages/astro/test/container.test.js b/packages/astro/test/container.test.js index 228844e7021d..6704aa03ff8f 100644 --- a/packages/astro/test/container.test.js +++ b/packages/astro/test/container.test.js @@ -139,4 +139,46 @@ describe('Container', () => { assert.match(result, /Custom name/); assert.match(result, /Bar name/); }); + + + it('Renders props', async () => { + const Page = createComponent( + (result, props, _slots) => { + return render`${renderComponent( + result, + 'BaseLayout', + BaseLayout, + {}, + { + default: () => render` + ${maybeRenderHead(result)} + ${props.isOpen ? "Is open" : "Is closed"} + `, + head: () => render` + ${renderComponent( + result, + 'Fragment', + Fragment, + { slot: 'head' }, + { + default: () => render``, + } + )} + `, + } + )}`; + }, + 'Component2.astro', + undefined + ); + + const container = await experimental_AstroContainer.create(); + const result = await container.renderToString(Page, { + props: { + isOpen: true + }, + }); + + assert.match(result, /Is open/); + }); });