withastro/astro · error · AstroError
ServerOnlyModule
ServerOnlyModule
Error message
The "astro:content" module is only available server-side.
What it means
Thrown when the `astro:content` virtual module is imported in a client-side (browser) context. The virtual module plugin's `load` hook checks the `isClient` flag; since content collections rely on filesystem access and server-only APIs, importing `astro:content` in client JavaScript is not supported and the `ServerOnlyModule` error is thrown.
Source
Thrown at packages/astro/src/content/vite-plugin-content-virtual-mod.ts:373
async function generateContentEntryFile({
settings,
isClient,
}: {
settings: AstroSettings;
fs: typeof nodeFs;
isClient: boolean;
}) {
const contentPaths = getContentPaths(
settings.config,
undefined,
settings.config.legacy?.collectionsBackwardsCompat,
);
const relContentDir = rootRelativePath(settings.config.root, contentPaths.contentDir);
let virtualModContents: string;
if (isClient) {
throw new AstroError({
...AstroErrorData.ServerOnlyModule,
message: AstroErrorData.ServerOnlyModule.message('astro:content'),
});
} else {
virtualModContents = nodeFs
.readFileSync(contentPaths.virtualModTemplate, 'utf-8')
.replace('@@CONTENT_DIR@@', relContentDir)
.replace(
'/* @@LIVE_CONTENT_CONFIG@@ */',
contentPaths.liveConfig.exists
? // Dynamic import so it extracts the chunk and avoids a circular import
`const liveCollections = (await import(${JSON.stringify(fileURLToPath(contentPaths.liveConfig.url))})).collections;`
: 'const liveCollections = {};',
);
}
return virtualModContents;
}View on GitHub (pinned to d081033d5f)
Solutions
- Move all `astro:content` imports to server-only code (`.astro` frontmatter, endpoints, or `server` islands).
- Pass collection data as props from the server to client components instead of importing `astro:content` in the client.
- If using `client:` directives, ensure the component does not directly import `astro:content`.
- Split shared utilities into a server-only module and a client-safe module.
Example fix
// before — src/components/BlogList.jsx (client component)
import { getCollection } from 'astro:content';
export default function BlogList() {
const posts = await getCollection('blog'); // crashes in browser
return <ul>{posts.map(p => <li>{p.id}</li>)}</ul>;
}
// after — src/components/BlogList.jsx
export default function BlogList({ posts }) {
return <ul>{posts.map(p => <li>{p.id}</li>)}</ul>;
}
// parent .astro file fetches on server and passes as prop Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure no client-side file imports astro:content
// In vite.config or astro.config, you can add a build-time check:
function assertNoClientContentImport(filePath: string, isClient: boolean) {
if (isClient) {
const content = readFileSync(filePath, 'utf-8');
if (content.includes("astro:content")) {
throw new Error(`${filePath} imports astro:content but runs client-side`);
}
}
} Type guard
// Server-only context check
function isServerSide(): boolean {
return typeof window === 'undefined';
} Prevention
- Never import astro:content in files bundled for the client.
- Fetch collection data in .astro frontmatter and pass as props.
- Audit client: directives on components that may transitively import astro:content.
When it happens
Trigger: The virtual module plugin's `load` hook is invoked with `isClient: true`. Code in a `.astro` component's client-side script, a `.js`/`.ts` file imported by client script, or a framework component (React/Vue/Svelte) running in the browser imports from `astro:content`.
Common situations: Trying to query content collections inside a React `useEffect` or Vue `onMounted`. Importing `getCollection` in a file that's bundled for the client. Accidentally adding `client:` directives to a component that imports `astro:content`. Moving server-side logic into a shared module that also gets imported client-side.
Related errors
- Astro components cannot be used in the browser. Tried to ren
- ActionCalledFromServerError
- Configured image service is not a local service
- FontFileUrlNotFound
- MissingGetFontFileRequestUrl
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/3ee4412df2fd358e.
Report an issue: GitHub.