This error occurs when your build tool or linter encounters JSX syntax but lacks the proper configuration to parse it. JSX must be transformed into regular JavaScript before browsers can execute it, requiring Babel presets or TypeScript compiler options.
The "Cannot parse JSX" error indicates that a tool in your development workflow—such as Babel, webpack, Jest, ESLint, or TypeScript—has encountered JSX syntax but doesn't have the necessary parser or transformer configured. JSX is not valid JavaScript; it's a syntax extension that must be compiled into standard JavaScript function calls (like React.createElement()) before execution. This error typically appears during build time (webpack/Vite), test time (Jest), or linting (ESLint). The specific error message varies by tool: webpack shows "Module parse failed: Unexpected token", Jest reports "Support for the experimental syntax 'jsx' isn't currently enabled", and ESLint displays "Parsing error: Unexpected token". The root cause is always the same: the tool processing your code needs explicit configuration telling it how to handle JSX syntax. Without the appropriate preset (@babel/preset-react), plugin, or compiler option, the parser treats angle brackets and JSX elements as invalid syntax.
First, ensure you have the necessary Babel packages installed. For modern React projects, you need the core Babel packages and the React preset:
npm install --save-dev @babel/core @babel/preset-env @babel/preset-reactIf using webpack, also install babel-loader:
npm install --save-dev babel-loaderFor Jest testing, babel-jest is usually installed automatically with Jest, but you can explicitly add it:
npm install --save-dev babel-jestThese packages provide the transformation pipeline that converts JSX into standard JavaScript.
Add the React preset to your Babel configuration. You can use .babelrc, babel.config.js, or the babel field in package.json.
Create or update .babelrc:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}Or use babel.config.js for more control:
module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-react'
]
};For TypeScript + React projects, include both:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
]
}The React preset enables JSX transformation and React-specific optimizations.
Update your webpack configuration to use babel-loader for JavaScript and JSX files. In webpack.config.js:
module.exports = {
module: {
rules: [
{
test: /\.jsx?$/, // Match both .js and .jsx files
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
}
]
},
resolve: {
extensions: ['.js', '.jsx'] // Allow importing without extension
}
};If you need to process JSX from node_modules (e.g., linked packages):
{
test: /\.jsx?$/,
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'node_modules/your-jsx-package')
],
use: 'babel-loader'
}This tells webpack to transform JSX before bundling.
If Jest can't parse JSX in your test files, ensure your Jest configuration includes the Babel transformer. In jest.config.js:
module.exports = {
transform: {
'^.+\\.(js|jsx)$': 'babel-jest',
},
moduleFileExtensions: ['js', 'jsx'],
};Or in package.json:
{
"jest": {
"transform": {
"^.+\\.(js|jsx)$": "babel-jest"
},
"moduleFileExtensions": ["js", "jsx"]
}
}Jest will automatically use your .babelrc configuration when babel-jest is configured. Clear the Jest cache after changing config:
npx jest --clearCacheIf ESLint shows parsing errors on JSX, configure it to use a parser that understands JSX. Install the parser:
npm install --save-dev @babel/eslint-parserUpdate .eslintrc.json:
{
"parser": "@babel/eslint-parser",
"parserOptions": {
"requireConfigFile": false,
"ecmaFeatures": {
"jsx": true
}
},
"extends": ["eslint:recommended", "plugin:react/recommended"]
}For TypeScript projects, use @typescript-eslint/parser instead:
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
}
}
}This allows ESLint to understand and lint JSX syntax properly.
For TypeScript projects, configure the JSX compiler option in tsconfig.json:
{
"compilerOptions": {
"jsx": "react-jsx", // For React 17+ with new JSX transform
"target": "ES2020",
"module": "esnext",
"lib": ["ES2020", "DOM"]
},
"include": ["src/**/*"]
}JSX option values:
- "react-jsx": Modern JSX transform (React 17+), no need to import React
- "react": Classic transform, requires import React from 'react'
- "preserve": Keep JSX syntax, let Babel handle transformation
Use .tsx file extension for TypeScript files containing JSX. TypeScript disallows angle bracket type assertions in .tsx files; use as instead:
// Wrong in .tsx
const myElement = <MyComponent>element as MyComponent;
// Correct in .tsx
const myElement = element as MyComponent;Ensure your React component files use the correct extensions:
- Use .jsx for JavaScript files with JSX
- Use .tsx for TypeScript files with JSX
- Or configure tools to process .js files containing JSX
If your build tool only processes .jsx files but you're using .js, either rename:
mv src/MyComponent.js src/MyComponent.jsxOr update your webpack/Vite config to process .js files with JSX:
// webpack
{
test: /\.js$/,
include: /src/,
use: 'babel-loader'
}Some tools (like Create React App) automatically process JSX in .js files, while others require explicit configuration.
After configuration changes, clear all caches and reinstall dependencies:
# Clear node_modules and lockfile
rm -rf node_modules package-lock.json
# Reinstall dependencies
npm install
# Clear Jest cache
npx jest --clearCache
# Clear webpack cache (if using webpack 5)
rm -rf node_modules/.cache
# Clear Babel cache
rm -rf node_modules/.cache/babel-loader
# Rebuild
npm run buildFor persistent issues, try legacy dependency resolution:
npm install --legacy-peer-depsRestart your development server and verify the build succeeds without parsing errors.
Version Compatibility: Ensure compatible versions across your toolchain. Babel 7+ requires @babel/preset-react (not the old babel-preset-react). React 17+ supports the new JSX transform that doesn't require importing React in every file—set "runtime": "automatic" in your preset config:
{
"presets": [
["@babel/preset-react", { "runtime": "automatic" }]
]
}Monorepo Considerations: In monorepos or when using linked npm packages, you may need to include those packages in babel-loader processing. Use the include option instead of just exclude: /node_modules/ to explicitly process specific packages.
Vite Users: Vite uses esbuild for fast builds but may require explicit JSX configuration. In vite.config.js, ensure the React plugin is configured:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
esbuild: {
loader: 'jsx',
include: /src\/.*\.jsx?$/,
}
})Production Builds: Parsing errors that only appear in production builds (not development) often indicate environment-specific Babel configurations. Check for .babelrc.production or environment-specific presets.
Prevention: Modern React project generators (Create React App, Vite, Next.js) configure JSX parsing automatically. Manual webpack setups require explicit configuration. Always test your configuration with both development and production builds, and include JSX tests in your Jest suite to catch parsing issues early.
Prop spreading could cause security issues
Prop spreading could cause security issues
Error: error:0308010C:digital envelope routines::unsupported
Error: error:0308010C:digital envelope routines::unsupported
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
React Hook useEffect placed inside a condition
Hook can only be called inside the body of a function component
Hook can only be called inside the body of a function component
Rollup failed to resolve import during build
How to fix "Rollup failed to resolve import" in React