JSX syntax errors occur when React encounters improperly formatted HTML-like code that violates JSX rules. These errors commonly arise from unclosed tags, missing parent wrappers, or using HTML syntax instead of JSX conventions.
This error occurs when the JSX parser encounters markup that doesn't conform to JSX syntax rules. Unlike standard HTML, JSX follows stricter XML-style syntax requirements and has specific naming conventions for attributes and elements. JSX is a syntax extension for JavaScript that looks similar to HTML but compiles to React.createElement() calls. Because it's ultimately JavaScript, it must follow stricter parsing rules than HTML. When you write invalid JSX syntax, the Babel transpiler or your development tools will reject it before it can be converted to valid JavaScript. The most common violations include: unclosed tags, multiple root elements without a wrapper, using HTML attribute names instead of JSX equivalents (like 'class' instead of 'className'), and self-closing tags that aren't properly formatted with the trailing slash.
If you're returning multiple elements, wrap them in a React Fragment or div:
// ❌ Wrong - multiple root elements
return (
<h1>Title</h1>
<p>Content</p>
);
// ✅ Correct - wrapped in Fragment
return (
<>
<h1>Title</h1>
<p>Content</p>
</>
);
// ✅ Also correct - explicit Fragment
return (
<React.Fragment>
<h1>Title</h1>
<p>Content</p>
</React.Fragment>
);Fragments don't create extra DOM nodes, making them preferable to unnecessary div wrappers.
JSX requires all tags to be explicitly closed, including void elements:
// ❌ Wrong - unclosed self-closing tags
<img src="photo.jpg">
<input type="text">
<br>
// ✅ Correct - properly closed
<img src="photo.jpg" />
<input type="text" />
<br />This applies to all void elements: img, input, br, hr, meta, link, area, base, col, embed, source, track, wbr.
Check that every opening tag has a corresponding closing tag with the correct name:
// ❌ Wrong - unclosed tag
<div>
<ul>
<li>Item 1
<li>Item 2</li>
</ul>
</div>
// ✅ Correct - all tags closed
<div>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>Pay special attention to nested structures where it's easy to miss closing tags.
Replace HTML attribute names with their JSX equivalents:
// ❌ Wrong - HTML attribute names
<div class="container">
<label for="email">Email:</label>
<button onclick={handleClick}>Submit</button>
</div>
// ✅ Correct - JSX attribute names
<div className="container">
<label htmlFor="email">Email:</label>
<button onClick={handleClick}>Submit</button>
</div>Key conversions:
- class → className
- for → htmlFor
- onclick → onClick (and all event handlers to camelCase)
- tabindex → tabIndex
- Any hyphenated attributes → camelCase
Replace HTML comment syntax with JSX-compatible comments:
// ❌ Wrong - HTML comments
<div>
<!-- This is a comment -->
<p>Content</p>
</div>
// ✅ Correct - JSX comments
<div>
{/* This is a comment */}
<p>Content</p>
</div>JSX comments must be wrapped in curly braces and use JavaScript comment syntax.
React distinguishes between HTML elements and components by capitalization:
// ❌ Wrong - lowercase component name
const myComponent = () => <div>Hello</div>;
<myComponent />
// ✅ Correct - capitalized component name
const MyComponent = () => <div>Hello</div>;
<MyComponent />Lowercase tags are treated as HTML elements, while capitalized names are treated as React components.
If using JSX syntax, ensure your files have the correct extension:
# Rename files containing JSX
mv MyComponent.js MyComponent.jsx
# For TypeScript
mv MyComponent.ts MyComponent.tsxUpdate any imports to reference the new file extension if your bundler requires it. Modern tools like Create React App handle this automatically, but some configurations may require explicit .jsx extensions.
Transpiler Configuration: If you must use JSX in .js files, configure your Babel preset to include '@babel/preset-react' or set up your bundler (Vite, webpack) to process JSX in .js files. However, using .jsx extensions is the recommended practice.
Fragment Short Syntax: The <></> syntax is shorthand for <React.Fragment>. Use the explicit form when you need to pass keys in lists: <React.Fragment key={item.id}>.
SVG Attributes: SVG elements in JSX also require camelCase: stroke-width becomes strokeWidth, fill-opacity becomes fillOpacity, etc.
Data Attributes: Data and aria attributes are exceptions to the camelCase rule - they remain lowercase with hyphens: data-id, aria-label.
Prevention Tips: Enable ESLint with the eslint-plugin-react plugin to catch JSX syntax errors during development. Modern editors like VS Code provide immediate feedback on JSX syntax issues when properly configured with TypeScript or Flow.
TypeScript JSX: When using TypeScript, the tsconfig.json must include "jsx": "react" or "jsx": "react-jsx" (React 17+) in compilerOptions to enable JSX parsing.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite