React App Not Starting? 7 Common Fixes for Development Servers
When a React development server won’t start, it’s rarely “React is broken.” It’s almost always a mismatch between your Node/npm environment, a corrupted dependency tree, a port/process conflict, a misconfigured script, or a tooling issue (Webpack/Vite/Babel/TypeScript). This tutorial walks through 7 common fixes with deep explanations and real commands you can run right now.
Before You Start: Identify What “Not Starting” Means
Different symptoms imply different root causes. Collect these details first:
- What command are you running?
- Create React App (CRA):
npm startoryarn start - Vite:
npm run dev - Next.js (React framework):
npm run dev
- Create React App (CRA):
- What’s the exact error message?
- Does the terminal hang, crash, or exit immediately?
- What OS are you on (Windows/macOS/Linux)?
- Node and package manager versions:
node -v npm -v yarn -v pnpm -v
If you’re unsure which tool you’re using, check package.json:
cat package.json
Look for scripts like "start", "dev", "serve", and dependencies like react-scripts (CRA) or vite.
Fix 1: Use a Compatible Node.js Version (and Align Tooling)
Why it breaks
React dev servers depend on a build toolchain (CRA’s react-scripts, Vite, Webpack, Babel, TypeScript). These tools often assume specific Node.js versions. If you’re on an unsupported Node version, you can see errors like:
ERR_OSSL_EVP_UNSUPPORTEDdigital envelope routines::unsupportedSyntaxError: Unexpected token '??'(older Node)Error: error:0308010C:digital envelope routines::unsupported(Node 17 + older Webpack)Cannot find module 'node:fs/promises'(Node too old)
What to do
- Check your Node version:
node -v - Check your project’s expected version:
- Look for
.nvmrc:cat .nvmrc - Or check
enginesinpackage.json:cat package.json
- Look for
Recommended Node versions (general guidance)
- CRA (react-scripts 5): Node 14/16/18 typically works; Node 20 can work but may expose dependency issues.
- Vite: usually supports modern Node (often Node 18+ recommended).
- Older CRA projects: may require Node 14/16.
Fix by switching Node versions (NVM)
macOS/Linux (nvm):
nvm install 18
nvm use 18
node -v
Windows (nvm-windows):
nvm install 18.20.0
nvm use 18.20.0
node -v
If you hit OpenSSL errors (common with older Webpack)
If you’re stuck on an older toolchain and Node 17+, you may temporarily run with:
export NODE_OPTIONS=--openssl-legacy-provider
npm start
On Windows PowerShell:
$env:NODE_OPTIONS="--openssl-legacy-provider"
npm start
Better long-term fix: upgrade the build tooling (e.g., CRA/react-scripts) or use a supported Node version.
Fix 2: Clean and Reinstall Dependencies (node_modules + lockfile)
Why it breaks
A React dev server is only as stable as your dependency tree. Common causes:
- Partial install (interrupted
npm install) - Conflicting dependency versions
- Corrupted
node_modules - Switching package managers (npm ↔ yarn ↔ pnpm)
- Git branch changes that alter dependencies without reinstalling
Symptoms include:
Cannot find module ...Module not found: Error: Can't resolve ...ERR_MODULE_NOT_FOUND- Random build errors that disappear on a clean install
The correct “clean reinstall” procedure
Pick one package manager and stick to it. Then remove node_modules and the lockfile and reinstall.
npm
rm -rf node_modules package-lock.json
npm cache verify
npm install
npm start
yarn (classic)
rm -rf node_modules yarn.lock
yarn cache clean
yarn install
yarn start
pnpm
rm -rf node_modules pnpm-lock.yaml
pnpm store prune
pnpm install
pnpm dev
Windows deletion commands
PowerShell:
Remove-Item -Recurse -Force node_modules
Remove-Item -Force package-lock.json
npm install
npm start
Why removing the lockfile helps (and when it hurts)
- Lockfiles (
package-lock.json,yarn.lock,pnpm-lock.yaml) pin exact dependency versions. - If the lockfile is corrupted or reflects an incompatible tree, reinstalling with a fresh lockfile can fix it.
- However, deleting lockfiles can change versions and introduce new issues. If this is a team project, prefer:
- Keep the lockfile committed
- Use
npm ci(clean install based strictly on lockfile)
Team-safe approach with npm:
rm -rf node_modules
npm ci
npm start
Fix 3: Resolve Port Conflicts and Zombie Processes
Why it breaks
Dev servers bind to a port (commonly 3000 for CRA, 5173 for Vite). If another process is already using that port, your server may:
- fail with
EADDRINUSE: address already in use - prompt to use another port (CRA does this)
- hang or exit depending on configuration
Sometimes the “other process” is actually a previous dev server that didn’t shut down cleanly.
Find what’s using the port
macOS/Linux
Check port 3000:
lsof -i :3000
Kill the process by PID:
kill -9 <PID>
Windows (PowerShell)
Find the PID:
netstat -ano | findstr :3000
Kill it:
taskkill /PID <PID> /F
Change the port explicitly
CRA
macOS/Linux:
PORT=3001 npm start
Windows PowerShell:
$env:PORT=3001
npm start
Vite
npm run dev -- --port 3001
If it starts but the browser can’t connect
Sometimes the server binds to localhost but your environment expects a different interface. You can try:
Vite:
npm run dev -- --host 0.0.0.0
CRA (less common, but possible via env):
HOST=0.0.0.0 npm start
Fix 4: Verify package.json Scripts and Project Type
Why it breaks
You might be running the wrong command for the project:
- CRA uses
react-scripts start - Vite uses
vite - Next.js uses
next dev - A library repo might not have a dev server at all
Also, scripts can be accidentally edited or broken during merges.
Inspect scripts
cat package.json
Typical examples:
CRA:
{
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test"
}
}
Vite:
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
Next.js:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
Fix common script issues
- Script missing: add it back or use the correct command.
- Wrong command: use
npm run devinstead ofnpm start(Vite/Next often usedev). - Local binaries not installed: if
react-scriptsorviteis missing, reinstall dependencies (Fix 2).
Confirm the binary exists
With npm, you can check:
ls node_modules/.bin | grep -E "react-scripts|vite|next"
On Windows PowerShell:
Get-ChildItem node_modules\.bin | Select-Object Name
If react-scripts isn’t there in a CRA project, the install is incomplete or the dependency is missing from package.json.
Fix 5: Fix Environment Variables and .env Problems
Why it breaks
React dev servers often load environment variables from .env files. A malformed .env can prevent startup, especially if:
- You used quotes incorrectly
- You included spaces around
= - You used unsupported variable names
- You referenced variables that crash your config (e.g., in
vite.config.js)
CRA and Vite handle .env differently:
- CRA only exposes variables prefixed with
REACT_APP_ - Vite only exposes variables prefixed with
VITE_
If you accidentally rely on an unexposed variable, your app might crash at runtime (white screen) or during build if used in config.
Validate .env formatting
A safe .env looks like:
REACT_APP_API_URL=https://api.example.com
REACT_APP_FEATURE_X=true
Common mistakes:
REACT_APP_API_URL = https://api.example.com # spaces can break parsing
REACT_APP_KEY="abc" # quotes sometimes cause issues depending on tooling
export REACT_APP_API_URL=... # not valid in .env files
Debug by temporarily disabling .env
Rename it:
mv .env .env.bak
npm start
If the server starts, the issue is in your environment variables or config that reads them.
CRA vs Vite variable access
CRA:
console.log(process.env.REACT_APP_API_URL);
Vite:
console.log(import.meta.env.VITE_API_URL);
If your dev server crashes due to config reading env
Vite config runs in Node (not the browser). If you do something like:
// vite.config.js
const apiUrl = process.env.VITE_API_URL; // might be undefined depending on loading
Use Vite’s loadEnv:
import { defineConfig, loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
return {
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
};
});
Fix 6: Address TypeScript/Babel/ESM Configuration Errors
Why it breaks
Modern React projects may use:
- TypeScript (
tsconfig.json) - Babel (
babel.config.js,.babelrc) - ESM modules (
"type": "module"inpackage.json) - Custom Webpack/Vite config
A small mismatch can stop the dev server before it even binds to a port.
Common errors:
SyntaxError: Cannot use import statement outside a moduleReferenceError: require is not defined in ES module scope- TypeScript:
TS2307 Cannot find module ... - Babel: plugin/preset not found
Step 1: Check if your project is ESM
Inspect package.json:
cat package.json | grep '"type"'
If you see:
"type": "module"
then Node treats .js files as ESM. That affects config files like:
vite.config.jspostcss.config.jstailwind.config.jsjest.config.js
If those config files use require(...), they may crash.
Fix ESM/CommonJS config mismatches
Options:
- Convert config files to ESM syntax:
import { defineConfig } from 'vite'; export default defineConfig({ /* ... */ }); - Rename config files to
.cjsto force CommonJS:postcss.config.cjstailwind.config.cjsvite.config.cjs(Vite supports this)
Example for Tailwind:
mv tailwind.config.js tailwind.config.cjs
mv postcss.config.js postcss.config.cjs
Step 2: Validate TypeScript configuration
If TypeScript compilation fails, dev servers often refuse to start (or start but show an overlay error).
Run:
npx tsc --noEmit
If you get module resolution errors, check tsconfig.json:
baseUrlpathsmoduleResolution
Also ensure your dependencies include types:
npm i -D @types/node
Step 3: Ensure Babel presets/plugins exist
If you see errors like:
Cannot find module '@babel/preset-react'Plugin/Preset files are not allowed to export objects...
Reinstall Babel dependencies or remove custom Babel config if you don’t need it. For CRA, custom Babel config is limited; CRA expects to manage Babel internally unless you eject or use overrides.
You can locate unexpected Babel configs:
ls -a | grep babel
Fix 7: Fix File Watcher Issues (Especially on Linux/WSL/Docker)
Why it breaks
Dev servers rely on file watchers to detect changes and sometimes even to complete startup. In certain environments (WSL, Docker bind mounts, network filesystems), file watching can fail or be extremely slow.
Symptoms:
- Server starts but never finishes “compiling…”
- Hot reload doesn’t work
- CPU spikes
- Errors referencing
ENOSPC(watch limit reached)
Linux: Increase inotify watch limit
If you see:
ENOSPC: System limit for number of file watchers reached
Run:
cat /proc/sys/fs/inotify/max_user_watches
Temporarily increase:
sudo sysctl fs.inotify.max_user_watches=524288
Persist it:
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
WSL: Prefer working inside the Linux filesystem
If your project is on /mnt/c/..., file watching can be unreliable. Move it to your WSL home directory:
mkdir -p ~/projects
cp -r /mnt/c/Users/<you>/path/to/app ~/projects/app
cd ~/projects/app
npm install
npm start
Docker: Use polling if necessary
For Webpack-based setups, polling can help. CRA supports:
CHOKIDAR_USEPOLLING=true npm start
Vite:
npm run dev -- --watch
And in vite.config.js you can configure polling:
export default {
server: {
watch: {
usePolling: true,
interval: 100,
},
},
};
Polling is less efficient but more reliable on mounted volumes.
A Practical Debug Flow (Use This Order)
If you want a repeatable approach, do this:
- Check versions
node -v npm -v - Run the dev command with verbose logging
- npm:
npm start --loglevel verbose - Vite:
npm run dev -- --debug
- npm:
- Clean reinstall
rm -rf node_modules package-lock.json npm install - Check port conflicts
lsof -i :3000 - Temporarily remove
.envmv .env .env.bak - Validate TypeScript
npx tsc --noEmit - Fix watcher limits if on Linux/WSL/Docker
This sequence resolves the majority of “React app won’t start” cases without random guessing.
Common Error Messages and What They Usually Mean
EADDRINUSE: address already in use
Another process is using the port. Use Fix 3.
react-scripts: command not found
Dependencies not installed or react-scripts missing. Use Fix 2 and Fix 4.
ERR_OSSL_EVP_UNSUPPORTED
Node/OpenSSL mismatch with older Webpack. Use Fix 1.
Cannot use import statement outside a module
ESM/CommonJS mismatch. Use Fix 6.
ENOSPC: System limit for number of file watchers reached
Increase inotify watch limit (Fix 7).
Blank page but dev server “starts”
The dev server is running, but the app crashes in the browser. Open DevTools Console and check runtime errors. Often caused by:
- missing env variables
- API URL undefined
- incorrect import paths
- TypeScript runtime assumptions
Bonus: When You Need a “Nuclear Reset” (Safe Checklist)
If you’ve tried everything and want a clean baseline:
- Ensure correct Node version:
node -v - Remove dependencies and caches:
rm -rf node_modules package-lock.json npm cache clean --force - Reinstall:
npm install - Start on a different port:
PORT=3001 npm start - If still failing, capture logs to a file:
npm start --loglevel verbose 2>&1 | tee devserver.log
Then search devserver.log for the first real error (often earlier than the last line).
Summary: The 7 Common Fixes
- Use a compatible Node.js version (and avoid OpenSSL/toolchain mismatches).
- Clean reinstall dependencies (remove
node_modules+ lockfile, reinstall). - Resolve port conflicts (kill zombie processes or change ports).
- Verify
package.jsonscripts (use the right command for CRA/Vite/Next). - Fix
.envand environment variables (prefix rules, formatting, config loading). - Correct TypeScript/Babel/ESM configuration (module system consistency).
- Fix file watcher issues (inotify limits, WSL filesystem placement, Docker polling).
If you share your exact startup command, your package.json scripts, and the full terminal error output, you can usually pinpoint the root cause quickly instead of iterating blindly.