diff --git a/.changeset/afraid-cups-deliver.md b/.changeset/afraid-cups-deliver.md
new file mode 100644
index 0000000000..edbb27bfec
--- /dev/null
+++ b/.changeset/afraid-cups-deliver.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fixes a bug in the Container API that was triggered when trying to render a client component with some `client:*` directives.
diff --git a/examples/container-with-vitest/src/components/ReactWrapper.astro b/examples/container-with-vitest/src/components/ReactWrapper.astro
index 73ac6baebd..07ee4c5b42 100644
--- a/examples/container-with-vitest/src/components/ReactWrapper.astro
+++ b/examples/container-with-vitest/src/components/ReactWrapper.astro
@@ -2,4 +2,4 @@
import Counter from './Counter.jsx';
---
-
+
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index 46490dfa8b..76ea2f01dc 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -3220,6 +3220,12 @@ export interface SSRResult {
response: AstroGlobal['response'];
request: AstroGlobal['request'];
actionResult?: ReturnType;
+ // Metadata used to signal Astro renderer to skip any client hydration, such as:
+ // - client directories
+ // - client entrypoint
+ // - renderer-url
+ skipHydration: boolean;
+
renderers: SSRLoadedRenderer[];
/**
* Map of directive name (e.g. `load`) to the directive script code
diff --git a/packages/astro/src/container/index.ts b/packages/astro/src/container/index.ts
index 53bc33e4b9..7ce6d89b10 100644
--- a/packages/astro/src/container/index.ts
+++ b/packages/astro/src/container/index.ts
@@ -14,6 +14,7 @@ import type {
SSRManifest,
SSRResult,
} from '../@types/astro.js';
+import { getDefaultClientDirectives } from '../core/client-directive/index.js';
import { ASTRO_CONFIG_DEFAULTS } from '../core/config/schema.js';
import { validateConfig } from '../core/config/validate.js';
import { Logger } from '../core/logger/core.js';
@@ -114,7 +115,7 @@ function createManifest(
entryModules: manifest?.entryModules ?? {},
routes: manifest?.routes ?? [],
adapterName: '',
- clientDirectives: manifest?.clientDirectives ?? new Map(),
+ clientDirectives: manifest?.clientDirectives ?? getDefaultClientDirectives(),
renderers: renderers ?? manifest?.renderers ?? [],
base: manifest?.base ?? ASTRO_CONFIG_DEFAULTS.base,
componentMetadata: manifest?.componentMetadata ?? new Map(),
@@ -435,6 +436,8 @@ export class experimental_AstroContainer {
pathname: url.pathname,
locals: options?.locals ?? {},
});
+ // client directives aren't needed in this case
+ renderContext.skipHydration = true;
if (options.params) {
renderContext.params = options.params;
}
diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts
index e6ac423648..de9ffcc6e4 100644
--- a/packages/astro/src/core/render-context.ts
+++ b/packages/astro/src/core/render-context.ts
@@ -42,6 +42,9 @@ export class RenderContext {
// The first route that this instance of the context attempts to render
originalRoute: RouteData;
+ // Metadata used to signal Astro renderer to skip any client hydration
+ skipHydration: boolean;
+
private constructor(
readonly pipeline: Pipeline,
public locals: App.Locals,
@@ -56,6 +59,7 @@ export class RenderContext {
public props: Props = {}
) {
this.originalRoute = routeData;
+ this.skipHydration = false;
}
/**
@@ -297,7 +301,7 @@ export class RenderContext {
}
async createResult(mod: ComponentInstance) {
- const { cookies, pathname, pipeline, routeData, status } = this;
+ const { cookies, pathname, pipeline, routeData, status, skipHydration } = this;
const { clientDirectives, inlinedScripts, compressHTML, manifest, renderers, resolve } =
pipeline;
const { links, scripts, styles } = await pipeline.headElements(routeData);
@@ -343,6 +347,7 @@ export class RenderContext {
request: this.request,
scripts,
styles,
+ skipHydration,
actionResult,
_metadata: {
hasHydrationScript: false,
diff --git a/packages/astro/src/runtime/server/hydration.ts b/packages/astro/src/runtime/server/hydration.ts
index 28b5ff674e..ca88b8bd64 100644
--- a/packages/astro/src/runtime/server/hydration.ts
+++ b/packages/astro/src/runtime/server/hydration.ts
@@ -156,7 +156,7 @@ export async function generateHydrateScript(
island.props['component-url'] = await result.resolve(decodeURI(componentUrl));
// Add renderer url
- if (renderer.clientEntrypoint) {
+ if (renderer.clientEntrypoint && !result.skipHydration) {
island.props['component-export'] = componentExport.value;
island.props['renderer-url'] = await result.resolve(decodeURI(renderer.clientEntrypoint));
island.props['props'] = escapeHTML(serializeProps(props, metadata));
diff --git a/packages/astro/src/runtime/server/render/component.ts b/packages/astro/src/runtime/server/render/component.ts
index 4473c7441f..6074282bbc 100644
--- a/packages/astro/src/runtime/server/render/component.ts
+++ b/packages/astro/src/runtime/server/render/component.ts
@@ -295,7 +295,8 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr
renderer &&
!renderer.clientEntrypoint &&
renderer.name !== '@astrojs/lit' &&
- metadata.hydrate
+ metadata.hydrate &&
+ !result.skipHydration
) {
throw new AstroError({
...AstroErrorData.NoClientEntrypoint,
diff --git a/packages/astro/test/container.test.js b/packages/astro/test/container.test.js
index 72d233ce47..a054b819c8 100644
--- a/packages/astro/test/container.test.js
+++ b/packages/astro/test/container.test.js
@@ -261,4 +261,12 @@ describe('Container with renderers', () => {
assert.match(html, /I am a vue button/);
});
+
+ it('Should render a component with directives', async () => {
+ const request = new Request('https://example.com/button-directive');
+ const response = await app.render(request);
+ const html = await response.text();
+
+ assert.match(html, /I am a react button/);
+ });
});
diff --git a/packages/astro/test/fixtures/container-custom-renderers/src/components/buttonDirective.astro b/packages/astro/test/fixtures/container-custom-renderers/src/components/buttonDirective.astro
new file mode 100644
index 0000000000..01b6a03dfa
--- /dev/null
+++ b/packages/astro/test/fixtures/container-custom-renderers/src/components/buttonDirective.astro
@@ -0,0 +1,5 @@
+---
+import Button from "./button.jsx"
+---
+
+
diff --git a/packages/astro/test/fixtures/container-custom-renderers/src/pages/button-directive.ts b/packages/astro/test/fixtures/container-custom-renderers/src/pages/button-directive.ts
new file mode 100644
index 0000000000..4e01203358
--- /dev/null
+++ b/packages/astro/test/fixtures/container-custom-renderers/src/pages/button-directive.ts
@@ -0,0 +1,10 @@
+import type {APIRoute, SSRLoadedRenderer} from "astro";
+import { experimental_AstroContainer } from "astro/container";
+import renderer from '@astrojs/react/server.js';
+import Component from "../components/buttonDirective.astro"
+
+export const GET: APIRoute = async (ctx) => {
+ const container = await experimental_AstroContainer.create();
+ container.addServerRenderer({ renderer });
+ return await container.renderToResponse(Component);
+}
diff --git a/packages/astro/test/fixtures/container-custom-renderers/src/pages/react.ts b/packages/astro/test/fixtures/container-custom-renderers/src/pages/react.ts
index d77eaead11..b8d44a3551 100644
--- a/packages/astro/test/fixtures/container-custom-renderers/src/pages/react.ts
+++ b/packages/astro/test/fixtures/container-custom-renderers/src/pages/react.ts
@@ -1,4 +1,4 @@
-import type {APIRoute, SSRLoadedRenderer} from "astro";
+import type {APIRoute} from "astro";
import { experimental_AstroContainer } from "astro/container";
import renderer from '@astrojs/react/server.js';
import Component from "../components/button.jsx"
diff --git a/packages/integrations/react/src/index.ts b/packages/integrations/react/src/index.ts
index f8a9c4ab25..a483ee4b43 100644
--- a/packages/integrations/react/src/index.ts
+++ b/packages/integrations/react/src/index.ts
@@ -60,6 +60,7 @@ function getViteConfiguration(
'react/jsx-runtime',
'react/jsx-dev-runtime',
'react-dom',
+ ``,
],
exclude: [reactConfig.server],
},