withastro/astro · error · Error

Astro components cannot be used in the browser. Tried to…

Error message

Astro components cannot be used in the browser.
Tried to render "${filename}".

What it means

Astro components are server-only — they compile to server rendering code. When Vite's client environment encounters an import of an .astro file, Astro substitutes an empty module whose default export (in dev) is a function that throws this error when called, turning 'I imported a server component into browser code' into a loud failure at the call site. Production builds return {} instead, so this surfaces mainly during development.

Solutions

  1. Remove the .astro import from client-executed code; render the Astro component on the server and pass its output into the island via props, slots, or set:html
  2. Replace the Astro component inside the island with a framework component (.tsx/.vue/etc.)
  3. If only markup is needed, pre-render it in the .astro page and inject it with set:html

Example fix

// before — src/islands/Counter.ts (hydrated in the browser)
import Card from '../components/Card.astro';
export function mount(el) {
	render(<Card />, el);
}

// after — render on the server, pass HTML through
// page.astro
<Counter el={document.querySelector('#x')}>
	<Card />  {/* rendered server-side, delivered as children */}
</Counter>
Defensive patterns

Strategy: validation

Validate before calling

// scripts/check-client-astro-imports.mjs — run in CI to fail before the browser does
import { execSync } from 'node:child_process';
try {
	const out = execSync(
		"grep -rn \"from ['\\\"].*\\.astro['\\\"]\" src --include='*.ts' --include='*.tsx' --include='*.vue' --include='*.svelte'",
		{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] },
	);
	if (out.trim()) {
		console.error('Client-side code imports .astro components:\n' + out);
		process.exit(1);
	}
} catch (e) {
	if (e.stdout) { console.error('Client-side code imports .astro components:\n' + e.stdout); process.exit(1); }
}

Prevention

When it happens

Trigger: import Card from './Card.astro' inside a <script> tag (which runs in the browser) or inside a framework island (.tsx/.svelte/.vue) that hydrates on the client; transitively importing an .astro file from client-side shared code; trying to render an Astro component from a React/Vue component at runtime.

Common situations: Attempting to use an Astro component inside a React island; sharing a module that re-exports values computed in .astro frontmatter; porting server-component patterns from other frameworks into client code.

Related errors


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/7a3725dfacd6a466. Report an issue: GitHub.

Appendix: source

Thrown at packages/astro/src/vite-plugin-astro/index.ts:273

						exclude: [specialQueriesRE, /(?:\?|&)astro(?:&|=|$)/],
					},
				},
				async handler(source, id) {
					const parsedId = parseAstroRequest(id);

					if (!parsedId.filename.endsWith('.astro')) {
						return;
					}

					const filename = normalizePath(parsedId.filename);

					// If an Astro component is imported in code used on the client, we return an empty
					// module so that Vite doesn’t bundle the server-side Astro code for the client.
					if (this.environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) {
						return {
							code: `export default import.meta.env.DEV
									? () => {
											throw new Error(
												'Astro components cannot be used in the browser.\\nTried to render "${filename}".'
											);
										}
									: {};`,
							moduleType: 'ts',
							meta: { vite: { lang: 'ts' } },
						};
					}

					const transformResult = await compile(source, filename);

					const astroMetadata: AstroPluginMetadata['astro'] = {
						// Remove Astro components that have been mistakenly given client directives
						// We'll warn the user about this later, but for now we'll prevent them from breaking the build
						clientOnlyComponents: transformResult.clientOnlyComponents.filter(notAstroComponent),
						hydratedComponents: transformResult.hydratedComponents.filter(notAstroComponent),
						serverComponents: transformResult.serverComponents,
						scripts: transformResult.scripts,

View on GitHub (pinned to 52e6c34790)