Remix "You cannot use X in a browser bundle" (server-only leak) in CI
Remix splits code into server and browser bundles. When a server-only module (database client, secret access, Node built-in) reaches the browser bundle, the build fails so a secret or Node API never ships to the client. The fix is to mark it server-only.
What this error means
The build reports that a server module or a Node built-in ended up in the browser bundle, or the client bundle fails resolving a Node-only import that a route re-exported.
Error: Server-only module referenced by client
'app/models/db.ts' imported by route 'app/routes/_index.tsx'
Convert the file to a '.server' module, or move the import into a loader/action.Common causes
A server module imported at module scope
A route imports a database or secrets helper at the top level, so it is included in the browser bundle instead of only running in the loader/action.
A shared file re-exports server-only symbols
A barrel file exports both client-safe and server-only code; importing it from a component drags the server code into the client.
How to fix it
Give server-only files a .server suffix
- Rename the module to end in
.server.tsso Remix excludes it from the browser bundle. - Import it only inside
loader/action, never in the component body. - Rebuild to confirm the browser bundle no longer references it.
// app/models/db.server.ts (renamed)
export const db = createClient(process.env.DATABASE_URL);Split barrels into client-safe and server-only
Do not re-export server code from a file the client imports. Keep server helpers in dedicated .server files.
How to prevent it
- Name every server-only module
*.server.ts/*.server.js. - Import server helpers only inside loaders and actions.
- Avoid barrel files that mix client-safe and server-only exports.