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
- 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
- Replace the Astro component inside the island with a framework component (.tsx/.vue/etc.)
- 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
- Keep .astro imports out of <script> tags and framework islands — Astro components render server-side only
- Pass pre-rendered markup into islands via children/slots or set:html instead of importing components
- Add a CI grep for .astro imports in client-side source files to catch regressions early
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
- ServerOnlyModule
- [astro:actions] `defineAction()` unexpectedly used on the…
- [astro:actions] `getActionContext()` unexpectedly used on…
- Could not render ` `. No matching import has been found for…
- Expected a matching import for component
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)