0

I'm having a persistent issue deploying a Vite + React (TypeScript) application with a Node.js (Express) backend to Vercel, and I'm caught in a frustrating loop.

The Tech Stack:

  • Frontend: Vite, React, TypeScript, React Router
  • Backend: Node.js, Express, TypeScript (in a single server.ts file at the root)
  • Deployment: Vercel

The Core Problem:

--> I'm facing two conflicting outcomes:

  1. Without vercel.json: The application builds and the root URL (/) loads correctly. However, if I navigate to any client-side route (e.g., /login, then get redirected to /mentor-dashboard) or refresh a page on a client-side route, Vercel returns a 404: NOT_FOUND error. This is expected SPA behavior that needs a rewrite rule.

  2. With vercel.json: To solve the 404 issue, I add a vercel.json file to handle the SPA routing. However, this results in a blank white screen for the entire application, even though the Vercel build log shows no errors. The network tab in my browser's dev tools shows that the index.html is served, but it seems unable to load the necessary JS/CSS assets.

This feels like a classic configuration issue, but I've tried multiple vercel.json setups without success.

Project Structure:

    1 /
    2 ├── dist/
    3 ├── node_modules/
    4 ├── src/
    5 │   ├── App.tsx
    6 │   ├── main.tsx
    7 │   └── ... (components, pages, etc.)
    8 ├── .gitignore
    9 ├── index.html
   10 ├── package.json
   11 ├── server.ts         <-- My Express API server
   12 ├── tsconfig.json
   13 ├── vercel.json       <-- The file in question
   14 └── vite.config.ts

Relevant Files:

package.json (scripts):

1 "scripts": {
   2   "dev": "vite",
   3   "build": "vite build",
   4   "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
   5   "preview": "vite preview",
   6   "start": "node dist/server.js"
   7 },

The vercel.json I've tried most recently: This version was intended to explicitly define the build and the routes.

    1 {
    2   "builds": [
    3     {
    4       "src": "package.json",
    5       "use": "@vercel/static-build",
    6       "config": { "distDir": "dist" }
    7     },
    8     {
    9       "src": "/server.ts",
   10       "use": "@vercel/node"
   11     }
   12   ],
   13   "routes": [
   14     { "src": "/api/(.*)", "dest": "/server.ts" },
   15     { "src": "/(.*)", "dest": "/index.html" }
   16   ]
   17 }

vite.config.ts:

   1 import react from "@vitejs/plugin-react";
   2 import { defineConfig } from "vite";
   3
   4 // https://vitejs.dev/config/
   5 export default defineConfig({
   6 plugins: [react()],
   7 });

My Question:

What is the correct vercel.json configuration to get Vercel to both:

  1. Correctly run the vite build command and recognize the dist directory as the source for static assets.
  2. Properly handle client-side routing for a Single-Page Application by rewriting all non-API requests to index.html?

I've been stuck on this for a while, and any help or insight would be greatly appreciated. Thank you

I've gone through a few iterations, which seem to be circling the core issue:

  1. Initial Attempt: No vercel.json

    • What I did: I removed the vercel.json file entirely, hoping Vercel's zero-config deployment would handle it.
    • What I expected: I expected Vercel to automatically detect the Vite project, run npm run build, and correctly serve the application from the dist directory.
    • What actually happened: The root of the site (/) loaded perfectly. However, any attempt to access a client-side route directly (like refreshing on /mentor-dashboard) resulted in a Vercel 404: NOT_FOUND page.
  2. Second Attempt: vercel.json with only routes

    • What I did: To fix the 404 error, I added a vercel.json with a simple rewrite rule to redirect all traffic to index.html.
    • What I expected: I thought Vercel would still build the project automatically and then apply my routing rule, solving the 404 issue.
    • What actually happened: This resulted in a blank white screen. It seems that adding vercel.json disabled Vercel's automatic framework detection, so the vite build command was never run, and the index.html file had no JS or CSS to load.
  3. Third Attempt: The "Complete" vercel.json

    • What I did: I created what I believed to be a complete vercel.json file, explicitly defining both the build process and the routing rules.
    1 {
    2   "builds": [
    3      { "src": "package.json", "use": "@vercel/static-build", "config"{"distDir":"dist" } },
    4      { "src": "/server.ts", "use": "@vercel/node" }
    5    ],
    6       "routes": [
    7          { "src": "/api/(.*)", "dest": "/server.ts" },
    8          { "src": "/(.*)", "dest": "/index.html" }
    9        ]
   10 }
  • What I expected: This should have been the solution. I expected Vercel to:

    1. Execute the vite build script found in package.json.
    2. Recognize that the output is in the dist directory.
    3. Apply the routes so that any request like /mentor-dashboard serves the index.html from the dist directory, allowing React Router to take over.
  • What actually happened: This still results in a blank white screen, even though the Vercel deployment log shows a successful build. This is the most confusing part. It seems the connection between the build output and the routing destination is broken.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.