Skip to main content

JSX Closing Tags Rule: Self-Closing Elements

JSX inherits strict XML-based syntax rules, requiring every opening tag to have an explicit closing tag. Unlike HTML, which allows lenient parsing and optional closing tags, JSX must be well-formed and unambiguous for Babel transpilation to succeed. Tags with children use closing tags (</p>), while empty void elements use self-closing syntax (<img />).

Key Takeaways

  • JSX is stricter than HTML: every opening tag requires an explicit closing tag or self-closing syntax
  • Tags with content use paired opening and closing tags (<div>...</div>)
  • Void elements (img, input, br, hr) and childless components use self-closing syntax with a forward slash (<img />)
  • Unclosed tags cause JSX transpilation errors and prevent your code from compiling
  • Using a linter catches these errors before runtime, saving debugging time

Prerequisites

Before we begin, ensure you have a solid grasp of the following concepts:

  • Basic HTML: Familiarity with standard HTML tags, including void elements like <img> and <br>.
  • Basic JSX Syntax: You should be comfortable writing simple JSX elements and understand JSX basics.
  • React Components: Understanding what functional components are and how they return JSX.

Why JSX Requires All Tags Be Closed

JSX's syntax is based on XML (eXtensible Markup Language), not HTML. XML is designed to be parsed unambiguously by machines, which means there are no optional closing tags. Every opening tag must have a corresponding closing tag.

HTML, by contrast, is more lenient. A browser's HTML parser will often correct mistakes—automatically closing a <p> tag when it sees a new paragraph tag. The JSX transpiler (Babel) does not do this. It requires well-formed, valid code to convert it into React.createElement() calls. An unclosed tag is a syntax error.

The Problem in Practice:

This HTML might render in a browser, but it is invalid JSX:

// This will cause a compilation error
function ImageList() {
return (
<>
<p>Look at these images:
<img src="image1.jpg">
<img src="image2.jpg">
</>
);
}

This code fails to compile because the <p> and <img> tags are not closed. Babel's parser rejects it before any JavaScript can execute.


Closing Tags With Children

For any element that wraps around other elements or text, you must provide an explicit closing tag. This is identical to well-formed HTML:

function Article() {
return (
<div>
<p>This is a paragraph with a closing tag.</p>
<ul>
<li>List item 1</li>
<li>List item 2</li>
</ul>
<h2>Section Heading</h2>
<p>Another paragraph.</p>
</div>
);
}

Every opening <p>, <ul>, <li>, and <h2> has a corresponding closing tag. This pattern is straightforward and matches standard HTML practices.


Self-Closing Tags for Empty Elements

In HTML, some elements are "void" or "empty," meaning they cannot have children. Common examples include <img>, <input>, <br>, <hr>, and <meta>. In HTML, you can write them as <br> or <img>.

In JSX, these tags must be "self-closed" by adding a forward slash / before the final greater-than sign >. This signals to Babel that the element has no children and needs no closing tag.

Correcting the invalid example from above:

function ImageList() {
return (
<>
<p>Look at these images:</p>
<img src="image1.jpg" alt="First image" />
<img src="image2.jpg" alt="Second image" />
<input type="text" placeholder="Enter text" />
<br />
</>
);
}

Notice that:

  • The <p> tag now has a closing </p> tag.
  • Both <img> tags are self-closed with />.
  • The <input> and <br> tags are also self-closed.

This is the only valid way to write void elements in JSX.


Self-Closing Custom React Components

This rule also applies to your own React components. If a component doesn't need to wrap other JSX (it has no children), use the self-closing syntax. It's cleaner and signals to other developers that the component stands alone.

Example:

// UserProfile.jsx
function UserProfile({ user }) {
return (
<div>
<img src={user.avatarUrl} alt={user.name} />
<p>{user.name}</p>
<p>{user.email}</p>
</div>
);
}

// Greeting.jsx
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}

// App.jsx
import UserProfile from './UserProfile';
import Greeting from './Greeting';

function App() {
const user = { name: 'Hedy', avatarUrl: '/hedy.jpg', email: '[email protected]' };

return (
<>
{/* These components have no children, so they are self-closed */}
<Greeting name={user.name} />
<UserProfile user={user} />
</>
);
}

Using <Greeting /> and <UserProfile /> is much cleaner than writing <Greeting></Greeting> when there is nothing to put inside. Self-closing syntax is the React convention for components without children.


Best Practices for Tag Closing

Always Self-Close Empty Elements: Make it a habit to immediately self-close any element that won't have children, such as <img>, <input>, <hr>, <br>, and childless components.

Rely on Your Linter: A good linter (like ESLint with the react plugin) will immediately flag unclosed tags as errors, helping you catch these mistakes before you run your code.

Format for Readability: For components with many props, put each prop on a new line. This makes the self-closing /> at the end very clear:

<img
className="avatar"
src={user.imageUrl}
alt={`Photo of ${user.name}`}
style={{ width: 100, height: 100 }}
/>

Frequently Asked Questions

What happens if I forget to close a tag in JSX?

Babel transpilation will fail with a syntax error. The error message usually points to the line with the missing closing tag or self-closing slash. You'll see a message like Unexpected token in your build output or IDE. Your code will not run until the tag is closed.

Can I use HTML5-style self-closing tags like <img> without the forward slash?

No. While <img> is valid HTML5, it is not valid JSX. JSX strictly requires <img /> with a forward slash. Always use the forward slash in JSX, even though modern HTML5 parsers accept both forms.

Do I need to self-close custom React components?

Yes, when they have no children. If you write <MyComponent> with no closing </MyComponent>, JSX will treat anything after it as children that should be wrapped by MyComponent. Always self-close components that don't accept children for clarity and correctness.

Is there a difference between <br> and <br /> in JSX?

In JSX, <br> is invalid and will cause a transpilation error. You must use <br />. The forward slash is mandatory in JSX, whereas some HTML linters might accept both.

What is a void element, and what are common examples?

A void element is an HTML element that cannot have any content or children. Common examples are <img>, <input>, <br>, <hr>, <meta>, <link>, and <source>. These must always be self-closed in JSX with />.


Further Reading