diff --git a/astro.sidebar.ts b/astro.sidebar.ts index f4424068a970b..f2bcf97c84029 100644 --- a/astro.sidebar.ts +++ b/astro.sidebar.ts @@ -138,6 +138,7 @@ export const sidebar = [ items: [ 'reference/integrations-reference', 'reference/adapter-reference', + 'reference/renderer-reference', 'reference/content-loader-reference', 'reference/image-service-reference', 'reference/dev-toolbar-app-reference', diff --git a/public/_redirects b/public/_redirects index 431a9b1af84bb..c9ed6c8034af6 100644 --- a/public/_redirects +++ b/public/_redirects @@ -56,8 +56,7 @@ # Very old docs site redirects # Once upon a time these URLs existed, so we try to keep them meaning something. -/reference/renderer-reference /en/reference/integrations-reference/ -/en/reference/renderer-reference /en/reference/integrations-reference/ +/reference/renderer-reference /en/reference/renderer-reference/ /en/core-concepts/component-hydration /en/concepts/islands/ /core-concepts/component-hydration /en/concepts/islands/ /core-concepts/collections /en/guides/content-collections/ diff --git a/src/content/docs/en/reference/integrations-reference.mdx b/src/content/docs/en/reference/integrations-reference.mdx index bd2b87e504452..930d45eedc9cd 100644 --- a/src/content/docs/en/reference/integrations-reference.mdx +++ b/src/content/docs/en/reference/integrations-reference.mdx @@ -194,11 +194,11 @@ export default {

-**Type:** (renderer: AstroRenderer) => void;
+**Type:** (renderer: AstroRenderer) => void;
**Examples:** [`svelte`](https://github.com/withastro/astro/blob/main/packages/integrations/svelte/src/index.ts), [`react`](https://github.com/withastro/astro/blob/main/packages/integrations/react/src/index.ts), [`preact`](https://github.com/withastro/astro/blob/main/packages/integrations/preact/src/index.ts), [`vue`](https://github.com/withastro/astro/blob/main/packages/integrations/vue/src/index.ts), [`solid`](https://github.com/withastro/astro/blob/main/packages/integrations/solid/src/index.ts)

-A callback function to add a component framework renderer (i.e. React, Vue, Svelte, etc). You can browse the examples and type definition above for more advanced options, but here are the 2 main options to be aware of: +A callback function to [add a component framework renderer](/en/reference/renderer-reference/) (i.e. React, Vue, Svelte, etc). You can browse the examples and type definition above for more advanced options, but here are the 2 main options to be aware of: - `clientEntrypoint` - path to a file that executes on the client whenever your component is used. This is mainly for rendering or hydrating your component with JS. - `serverEntrypoint` - path to a file that executes during server-side requests or static builds whenever your component is used. These should render components to static markup, with hooks for hydration where applicable. [React's `renderToString` callback](https://react.dev/reference/react-dom/server/renderToString) is a classic example. @@ -1438,7 +1438,6 @@ import type { AstroIntegrationMiddleware, AstroMiddlewareInstance, AstroPrerenderer, - AstroRenderer, ClientDirectiveConfig, HookParameters, IntegrationResolvedRoute, @@ -1447,8 +1446,6 @@ import type { RoutePart, RouteType, SSRComponentMetadata, - SSRLoadedRenderer, - SSRLoadedRendererValue, SSRManifest, ValidRedirectStatus, } from "astro"; @@ -1608,42 +1605,6 @@ Defines an optional method describing how to render a page. This will be called Defines an optional method called once all pages are pre-rendered. This is useful for performing cleanup tasks such as stopping a preview server. -### `AstroRenderer` - -

- -**Type:** `{ name: string; clientEntrypoint?: string | URL; serverEntrypoint: string | URL; }` -

- -Describes a [component framework renderer added by an integration](#addrenderer-option). - -#### `AstroRenderer.name` - -

- -**Type:** `string` -

- -The name of the component framework renderer. - -#### `AstroRenderer.clientEntrypoint` - -

- -**Type:** `string | URL` -

- -Defines the import path of the renderer that runs on the client whenever your component is used. - -#### `AstroRenderer.serverEntrypoint` - -

- -**Type:** `string | URL` -

- -Defines the import path of the renderer that runs during server-side requests or static builds whenever your component is used. - ### `ClientDirectiveConfig`

@@ -2028,152 +1989,6 @@ A description of how to render head content from this component, including wheth Determines whether the component contains the head content. -### `SSRLoadedRenderer` - -

- -**Type:** `{ name: string; clientEntrypoint?: string | URL; ssr: SSRLoadedRendererValue; }` -

- -Describes a renderer available for the server to use. This is a subset of [`AstroRenderer`](#astrorenderer) with additional properties. - -#### `SSRLoadedRenderer.ssr` - -

- -**Type:** [`SSRLoadedRendererValue`](#ssrloadedrenderervalue) -

- -Defines the functions and configuration used by the server for this framework. - -### `SSRLoadedRendererValue` - -Contains the functions and configuration necessary to render components on the server from a specific UI framework. - -#### `SSRLoadedRendererValue.name` - -

- -**Type:** `string` -

- -Specifies the name identifier for the renderer. - -#### `SSRLoadedRendererValue.check()` - -

- -**Type:** `(Component: any, props: Record, children: Record, metadata?: AstroComponentMetadata) => Promise` -

- -Determines whether the renderer should handle the component. Called for each registered renderer until one returns `true`. - -The function receives the component constructor or function, its props, slot content, and an optional [`metadata`](#astrocomponentmetadata) object containing information about the component's hydration directive and source. - -#### `SSRLoadedRendererValue.renderToStaticMarkup()` - -

- -**Type:** `(Component: any, props: Record, children: Record, metadata?: AstroComponentMetadata) => Promise<{ html: string; attrs?: Record; }>` -

- -Renders a framework component to static HTML markup on the server. - -The function receives the component constructor or function, its props, slot content, and an optional [`metadata`](#astrocomponentmetadata) object. Renderers can use `metadata` to determine whether the component will be hydrated on the client, which can be useful for conditionally including client-side hydration state. - -#### `SSRLoadedRendererValue.supportsAstroStaticSlot` - -

- -**Type:** `boolean`
- -

- -Indicates whether the renderer supports Astro's static slot optimization. When true, Astro prevents the removal of nested slots within islands. - -#### `SSRLoadedRendererValue.renderHydrationScript()` - -

- -**Type:** `() => string`
- -

- -Returns a framework-specific hydration script that must be injected into the HTML before the first component that uses this renderer. - -### `AstroComponentMetadata` - -

- -**Type:** `{ displayName: string; hydrate?: 'load' | 'idle' | 'visible' | 'media' | 'only'; hydrateArgs?: any; componentUrl?: string; componentExport?: { value: string; namespace?: boolean }; astroStaticSlot: true; }` -

- -An object passed as the fourth argument to a renderer's [`check()`](#ssrloadedrenderervaluecheck) and [`renderToStaticMarkup()`](#ssrloadedrenderervaluerendertostaticmarkup) functions. Contains information about the component being rendered, including its hydration directive. - -#### `AstroComponentMetadata.displayName` - -

- -**Type:** `string` -

- -The display name of the component, used for error messages and debugging. - -#### `AstroComponentMetadata.hydrate` - -

- -**Type:** `'load' | 'idle' | 'visible' | 'media' | 'only' | undefined` -

- -The [client directive](/en/reference/directives-reference/#client-directives) used on the component, if any. When `undefined`, the component will not be hydrated on the client. - -Renderers can use this value to conditionally include client-side hydration state. For example, a renderer can skip serializing transfer state for components that will not be hydrated: - -```ts -async function renderToStaticMarkup(Component, props, children, metadata) { - const willHydrate = !!metadata?.hydrate; - // Skip serializing hydration state if the component won't be hydrated - return render(Component, props, { includeTransferState: willHydrate }); -} -``` - -#### `AstroComponentMetadata.hydrateArgs` - -

- -**Type:** `any` -

- -Additional arguments for the hydration directive. For example, the value of `client:media="(max-width: 768px)"` would be `"(max-width: 768px)"`. For `client:only="react"`, the value would be `"react"`. - -#### `AstroComponentMetadata.componentUrl` - -

- -**Type:** `string | undefined` -

- -The URL of the component's source file. - -#### `AstroComponentMetadata.componentExport` - -

- -**Type:** `{ value: string; namespace?: boolean } | undefined` -

- -Information about the component's export. The `value` property is the name of the export (e.g. `"default"` for default exports). - -#### `AstroComponentMetadata.astroStaticSlot` - -

- -**Type:** `true` -

- -Always `true`. Indicates that Astro supports the static slot optimization for this component. Renderers that set [`supportsAstroStaticSlot`](#ssrloadedrenderervaluesupportsastrostaticslot) to `true` can use this in combination with [`hydrate`](#astrocomponentmetadatahydrate) to determine how to render slots. - ### `SSRManifest` An object containing build configuration and project metadata that the server adapters use at runtime to serve on-demand rendered pages. @@ -2314,7 +2129,7 @@ Specifies the [configured prefix for Astro-generated asset links](/en/reference/

-**Type:** SSRLoadedRenderer[] +**Type:** SSRLoadedRenderer[]

A list of renderers (e.g. React, Vue, Svelte, MDX) available for the server to use. diff --git a/src/content/docs/en/reference/renderer-reference.mdx b/src/content/docs/en/reference/renderer-reference.mdx new file mode 100644 index 0000000000000..043874cdd19b0 --- /dev/null +++ b/src/content/docs/en/reference/renderer-reference.mdx @@ -0,0 +1,338 @@ +--- +title: Astro Renderer API +sidebar: + label: Renderer API +i18nReady: true +tableOfContents: + minHeadingLevel: 2 + maxHeadingLevel: 3 +--- +import Since from '~/components/Since.astro'; +import { Tabs, TabItem } from '@astrojs/starlight/components'; +import ReadMore from '~/components/ReadMore.astro'; + +Astro is designed to support any UI framework and content format. This ability is powered by renderers, which are [integrations](/en/reference/integrations-reference/). See the [front-end frameworks](/en/guides/framework-components/) to learn how to use UI components from different frameworks in Astro. + +## What is a renderer? + +A renderer is a special kind of [integration](/en/reference/integrations-reference/) that teaches Astro how to render a specific file format (e.g. a framework component or a MDX file). A renderer consists of two parts: +- a server module imported during development and production builds to render components to HTML +- an optional client module imported in the browser to hydrate components with [client directives](/en/reference/directives-reference/#client-directives). + +## Building a renderer + +When you need to add support for a new file format in Astro, create an integration and call `addRenderer()` in the [`astro:config:setup`](/en/reference/integrations-reference/#astroconfigsetup) hook. This allows you to define the [server entrypoint](#building-a-server-entrypoint) that Astro should use for rendering components. Optionally, you can also define the [client entrypoint](#building-a-client-entrypoint) used for hydration. + +The following example shows how to register a renderer in an Astro integration: + +```ts title="my-renderer/index.js" {5-11} +export default function createIntegration() { + return { + name: "@example/my-renderer", + hooks: { + "astro:config:setup": ({ addRenderer }) => { + addRenderer({ + name: "@example/my-renderer", + clientEntrypoint: "@example/my-renderer/client.js", + serverEntrypoint: "@example/my-renderer/server.js", + }); + }, + }, + }; +} +``` + +## Building a server entrypoint + +You will need to create a file that runs during server-side rendering and defines how to render the file format or the component syntax. The server module must default-export an object that implements the [`SSRLoadedRendererValue`](#ssrloadedrenderervalue) interface. + +The following example shows a minimal server-side renderer implementing `check()` and `renderToStaticMarkup()`: + +```ts title="my-renderer/server.ts" +import type { AstroComponentMetadata, NamedSSRLoadedRendererValue } from 'astro'; + +async function check(Component: unknown) { + return typeof Component === 'function' && Boolean((Component as any).$$exampleRenderer); +} + +async function renderToStaticMarkup( + Component: any, + props: Record, + slots: Record, + metadata?: AstroComponentMetadata, +) { + const rendered = Component(props); + + return { + attrs: metadata?.hydrate ? { 'data-my-renderer': 'true' } : undefined, + html: `<${rendered.tag}>${rendered.text}${slots.default ?? ''}`, + }; +} + +const renderer: NamedSSRLoadedRendererValue = { + name: 'my-renderer', + check, + renderToStaticMarkup, +}; + +export default renderer; +``` + +## Building a client entrypoint + +When your renderer supports [client directives](/en/reference/directives-reference/#client-directives), create a client entrypoint that defines how to hydrate components in the browser. The client module must default-export a function that receives the island element and returns an async hydrator function. + +```ts title="my-renderer/client.ts" +export default (element: HTMLElement) => + async ( + Component: any, + props: Record, + slots: Record, + { client }: { client: string }, + ) => { + const rendered = Component({ ...props, slots }); + + if (client === 'only') { + element.innerHTML = ''; + } + + const node = document.createElement(rendered.tag); + node.textContent = rendered.text; + element.appendChild(node); + + element.addEventListener('astro:unmount', () => node.remove(), { once: true }); + }; +``` + +## Renderer types reference + +The following types can be imported from the `astro` module: + +```ts +import type { + AstroComponentMetadata, + AstroRenderer, + AsyncRendererComponentFn, + NamedSSRLoadedRendererValue, + SSRLoadedRenderer, + SSRLoadedRendererValue, +} from "astro"; +``` + +### `AstroComponentMetadata` + +

+ +**Type:** `{ displayName: string; hydrate?: 'load' | 'idle' | 'visible' | 'media' | 'only'; hydrateArgs?: any; componentUrl?: string; componentExport?: { value: string; namespace?: boolean }; astroStaticSlot: true; }` +

+ +An object passed as the fourth argument to a renderer's [`check()`](#ssrloadedrenderervaluecheck) and [`renderToStaticMarkup()`](#ssrloadedrenderervaluerendertostaticmarkup) functions. Contains information about the component being rendered, including its hydration directive. + +#### `AstroComponentMetadata.displayName` + +

+ +**Type:** `string` +

+ +Defines the component name Astro uses in diagnostics and error messages. + +#### `AstroComponentMetadata.hydrate` + +

+ +**Type:** `'load' | 'idle' | 'visible' | 'media' | 'only' | undefined` +

+ +Defines the [client directive](/en/reference/directives-reference/#client-directives) used on the component, if any. When `undefined`, the component will not be hydrated on the client. + +Renderers can use this value to conditionally include client-side hydration state. For example, a renderer can skip serializing transfer state for components that will not be hydrated: + +```ts +async function renderToStaticMarkup(Component, props, children, metadata) { + const willHydrate = !!metadata?.hydrate; + // Skip serializing hydration state if the component won't be hydrated + return render(Component, props, { includeTransferState: willHydrate }); +} +``` + +#### `AstroComponentMetadata.hydrateArgs` + +

+ +**Type:** `any` +

+ +Specifies the additional arguments passed to the hydration directive. For example, the value of `client:media="(max-width: 768px)"` would be `"(max-width: 768px)"`. For `client:only="react"`, the value would be `"react"`. + +#### `AstroComponentMetadata.componentUrl` + +

+ +**Type:** `string` +

+ +Defines the URL of the component's source file. + +#### `AstroComponentMetadata.componentExport` + +

+ +**Type:** `{ value: string; namespace?: boolean }` +

+ +Describes the component's export Astro will load on the client for hydrated components. + +##### `AstroComponentMetadata.componentExport.namespace` + +

+ +**Type:** `boolean` +

+ +Indicates whether the export is a namespace export. + +##### `AstroComponentMetadata.componentExport.value` + +

+ +**Type:** `string` +

+ +Indicates the name of the export (e.g. `"default"` for default exports). + +#### `AstroComponentMetadata.astroStaticSlot` + +

+ +**Type:** `true` +

+ +Always `true`. Indicates that Astro supports the static slot optimization for this component. Renderers that set [`supportsAstroStaticSlot`](#ssrloadedrenderervaluesupportsastrostaticslot) to `true` can use this in combination with [`hydrate`](#astrocomponentmetadatahydrate) to determine how to render slots. + +### `AstroRenderer` + +

+ +**Type:** `{ name: string; clientEntrypoint?: string | URL; serverEntrypoint: string | URL; }` +

+ +Describes a [renderer added by an integration](/en/reference/integrations-reference/#addrenderer-option). + +#### `AstroRenderer.name` + +

+ +**Type:** `string` +

+ +Defines the unique renderer's public name. This is used in error messages, logs, and for custom `client:only` hints such as ``. + +#### `AstroRenderer.clientEntrypoint` + +

+ +**Type:** `string | URL` +

+ +Defines the import path of the renderer that runs on the client whenever your component is used. + +#### `AstroRenderer.serverEntrypoint` + +

+ +**Type:** `string | URL` +

+ +Defines the import path of the renderer that runs during server-side requests or static builds whenever your component is used. + +### `AsyncRendererComponentFn` + +

+ +**Type:** `(Component: any, props: any, slots: Record, metadata?: AstroComponentMetadata) => Promise` +

+ +Takes the component's constructor or function, its props, the slot contents, and an optional [`metadata`](#astrocomponentmetadata) object, then returns a promise that resolves to a value matching the passed generic type. + +Renderers can use `metadata` to determine whether the component will be hydrated on the client, which can be useful for conditionally including client-side hydration state. + +### `NamedSSRLoadedRendererValue` + +Extends [`SSRLoadedRendererValue`](#ssrloadedrenderervalue) with a required `name` property. + +### `SSRLoadedRenderer` + +

+ +**Type:** `Pick & { ssr: SSRLoadedRendererValue; }` +

+ +Describes a renderer available for the server to use. This is a subset of [`AstroRenderer`](#astrorenderer) with additional properties. + +#### `SSRLoadedRenderer.ssr` + +

+ +**Type:** [`SSRLoadedRendererValue`](#ssrloadedrenderervalue) +

+ +Defines the functions and configuration used by the server for this framework. + +### `SSRLoadedRendererValue` + +

+ +**Type:** `{ name?: string; check: AsyncRendererComponentFn; renderToStaticMarkup: AsyncRendererComponentFn<{ html: string; attrs?: Record; }>; supportsAstroStaticSlot?: boolean; renderHydrationScript?: () => string; }` +

+ +Describes the server renderer module that Astro loads from `serverEntrypoint`. + +#### `SSRLoadedRendererValue.name` + +

+ +**Type:** `string` +

+ +Defines the renderer name when the server renderer needs to carry its own name. When not provided, Astro will reuse the name defined in [`AstroRenderer`](#astrorenderer). + +#### `SSRLoadedRendererValue.check()` + +

+ +**Type:** `AsyncRendererComponentFn` +

+ +Determines whether the renderer can handle a component. Return `true` only for compatible components. Return `false` for everything else. + +#### `SSRLoadedRendererValue.renderToStaticMarkup()` + +

+ +**Type:** `AsyncRendererComponentFn<{ html: string; attrs?: Record; }>` +

+ +Renders a compatible component to an HTML string on the server. + +Return `attrs` to add renderer-specific attributes to the generated `` element when the component hydrates. + +#### `SSRLoadedRendererValue.supportsAstroStaticSlot` + +

+ +**Type:** `boolean`
+ +

+ +Indicates whether the renderer supports Astro's static slot optimization. When true, Astro prevents the removal of nested slots within islands. + +#### `SSRLoadedRendererValue.renderHydrationScript()` + +

+ +**Type:** `() => string`
+ +

+ +Returns an HTML string to inject once per page before the first hydrated component handled by this renderer. This is useful for renderers that need page-level hydration setup.