How to Fix 404 Errors When Deploying Remix Apps to Vercel
Configure the official Vercel Remix preset to enable server-side routing and eliminate 404s

I was working on a Remix application that worked perfectly in development, but after deploying to Vercel I kept getting 404 errors on routes like /admin/login
. The app would build successfully, but any direct navigation to routes would fail with a 404, even though the same routes worked fine locally.
After digging through Vercel's documentation and dealing with several deployment failures, I discovered the solution lies in properly configuring Remix for Vercel's serverless environment using their official tools. This guide walks you through the exact steps to eliminate those frustrating 404s.
The Root Problem
Vercel needs to understand how to handle Remix's server-side rendering and routing. By default, Vercel treats your deployment as static files, but Remix routes require server-side processing. Without proper configuration, Vercel doesn't know that routes like /admin/login
should be handled by your Remix application.
Step 1: Install the Official Vercel Remix Package
The key to solving this is using Vercel's official Remix integration. Install the @vercel/remix
package:
pnpm add @vercel/remix
This package provides Vercel-specific utilities and, most importantly, a Vite preset that configures everything needed for proper deployment.
Step 2: Configure the Vercel Preset in Vite
Update your vite.config.ts
to include the Vercel preset:
// File: vite.config.ts
import { vitePlugin as remix } from "@remix-run/dev";
import { installGlobals } from "@remix-run/node";
import { defineConfig, type UserConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import { vercelPreset } from "@vercel/remix/vite";
installGlobals({ nativeFetch: true });
export default defineConfig({
plugins: [
remix({
presets: [vercelPreset()],
ignoredRouteFiles: ["**/.*"],
future: {
v3_fetcherPersist: true,
v3_relativeSplatPath: true,
v3_throwAbortReason: true,
v3_lazyRouteDiscovery: true,
v3_singleFetch: true,
v3_routeConfig: true,
},
}),
tsconfigPaths(),
],
build: {
assetsInlineLimit: 0,
},
}) satisfies UserConfig;
The vercelPreset()
is the crucial addition here. This preset automatically configures Remix to work with Vercel Functions, handling the server-side rendering setup that makes your routes accessible in production.
Step 3: Handle Package Version Conflicts
One major gotcha I encountered was version conflicts between Remix packages and @vercel/remix
. If you see peer dependency warnings during installation, you need to ensure all your Remix packages match the version expected by @vercel/remix
.
Check what version @vercel/remix
expects:
pnpm info @vercel/remix peerDependencies
Then align your Remix packages to match (example versions):
pnpm add @remix-run/dev@2.16.7 @remix-run/node@2.16.7 @remix-run/react@2.16.7 @remix-run/serve@2.16.7
This alignment ensures compatibility between Vercel's integration and your Remix installation.
Step 4: Configure Vercel Build Settings (Optional)
If you're still getting build failures due to peer dependency conflicts, you may need to override Vercel's install command. Create a vercel.json
in your project root:
{
"installCommand": "npm install --legacy-peer-deps"
}
This tells Vercel to ignore peer dependency conflicts during installation. While not ideal, it's sometimes necessary when dealing with version mismatches between the official Vercel integration and the latest Remix releases.
Step 5: Verify Your Build
Test your build locally to ensure everything works:
pnpm run build
You should see output indicating both client and server builds completed successfully, with no errors about routing or server configuration.
Why This Approach Works
The Vercel preset handles several critical configurations automatically:
- Server-side rendering setup: Configures Remix to work with Vercel's serverless functions
- Route handling: Ensures all your Remix routes are properly mapped to Vercel's routing system
- Asset optimization: Configures static asset serving with appropriate caching headers
- Function deployment: Packages your Remix app as Vercel Functions that can handle dynamic requests
Without this preset, Vercel treats your Remix app as a static site, which explains why direct navigation to routes results in 404 errors.
The Key Gotcha: Build Command Overrides
One important lesson from my experience: if you've previously configured custom build commands in Vercel's dashboard, make sure to remove them. I had overridden the build command to use npm instead of the detected package manager, which caused conflicts with the dependency resolution.
Always let Vercel auto-detect your package manager and use the default build process when possible.
Conclusion
The 404 errors you encounter when deploying Remix apps to Vercel stem from improper server-side rendering configuration. By using the official @vercel/remix
package and its Vite preset, you get a zero-configuration setup that properly handles routing in Vercel's serverless environment.
The key steps are installing the official package, adding the preset to your Vite config, and ensuring package version compatibility. With these changes, your Remix routes will work identically in both development and production.
Let me know in the comments if you have questions, and subscribe for more practical development guides.
Thanks, Matija