Skip to content
Latchkey

Remix/React Router "remix vite:build" Fails - Fix Server Build in CI

Remix (now React Router framework mode) builds a client bundle and a server bundle from the same routes. The build fails most often when a server-only dependency (a database client, fs, a secret-reading module) leaks into the client graph, or a build-time dependency is missing.

What this error means

A remix vite:build / react-router build fails with a resolve error for a Node builtin in the client bundle (Cannot resolve "fs"), or a server module being included client-side. It surfaces at build time, not in remix dev.

remix output
Error: The following dependencies are imported but could not be resolved:
  fs (imported by app/routes/admin.tsx)
Are they installed?
  x Build failed in 1.20s

Common causes

Server-only code imported into a client module

A module using fs/a DB client is imported at the top level of a route that also runs on the client, so the client build tries (and fails) to bundle a Node-only dependency.

Build-time dependency missing

A loader/build dependency is not installed, so a clean npm ci build cannot resolve it.

How to fix it

Isolate server-only modules

Keep server-only code in .server modules (or only inside loader/action) so it never enters the client graph.

app/routes/admin.tsx
// app/db.server.ts  - the .server suffix keeps it out of the client bundle
import { Pool } from 'pg'
export const pool = new Pool()

// app/routes/admin.tsx
export async function loader() {
  const { pool } = await import('~/db.server')
  // ...
}

Install missing build dependencies

Terminal
npm install <missing-dep>
npm ls <missing-dep>   # confirm it's a direct dependency

How to prevent it

  • Put server-only code in .server modules.
  • Access DB/fs only inside loader/action, never at module top level of shared routes.
  • Run the production build in CI so client/server leaks fail before deploy.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →