Discover JSX in React & Master the HTML-in-JavaScript Syntax
If you‘ve worked with React, you‘ve likely come across the enigmatic JSX syntax that looks like a mashup of JavaScript and HTML. JSX can seem confusing at first glance, but once you understand the fundamentals it becomes a powerful tool for building maintainable and readable React components. In this guide, we‘ll dive deep into JSX and learn everything you need to know to use it effectively in your React projects.
What is JSX?
JSX, which stands for JavaScript XML, is a syntax extension for JavaScript that was created for use with the React library. It allows you to write HTML-like code directly in your JavaScript files. With JSX, you can keep your component rendering logic and the corresponding markup in the same place, instead of artificially separating technologies.
Here‘s a basic example of what JSX looks like:
const heading = ;
This snippet may look like HTML, but it‘s actually valid JavaScript code thanks to JSX. The JSX compiler will translate this into a regular JavaScript function call:
const heading = React.createElement(‘h1‘, null, ‘Hello World!‘);
So why use JSX instead of calling React.createElement directly? The main advantages are:
-
Readability: JSX is easier to read and understand, especially for people already familiar with HTML and XML-like syntax.
-
Maintainability: Keeping markup and JavaScript logic together in components makes the code more maintainable and less error-prone.
-
Tooling: The React ecosystem has great tools and IDE support tailored for JSX, like linters, type checkers, and autocomplete.
Now that we have a high-level understanding of JSX, let‘s dive into the details of the syntax.
JSX Syntax Fundamentals
Here are the key rules and concepts you need to know to read and write JSX code:
JSX Expressions and Embedding JavaScript
Any valid JavaScript expression can be used inside JSX by wrapping it in curly braces { }. This allows you to embed variables, function calls, and more right in your markup. For example:
const name = ‘John‘;
const heading = ;
The name variable is embedded in the JSX heading element, and will be evaluated and replaced with its value when rendered.
JSX Elements and Components
JSX elements represent DOM nodes in the same way HTML tags do, but they are actually JavaScript function calls that construct the DOM behind the scenes. For instance, `represents anh1` node with the text content "Hello World!".
JSX also allows you to define your own custom "components" that work like reusable custom HTML tags. A JSX component is just a JavaScript function that returns JSX markup:
function Greeting(props) {
return ;
}
const element = <Greeting name="John" />;
Here, Greeting is a custom JSX component that can be used like a custom <Greeting> tag. The name="John" part represents a "prop" that is passed to the component function.
JSX Attributes and Props
Like HTML elements, JSX elements can have attributes to configure them. However, there are a few key differences:
- Attribute names use camelCase instead of kebab-case (e.g.
onClickinstead ofonclick) - The
classattribute is renamed toclassNameto avoid collision with the JS reserved word - Inline styles are provided as objects instead of strings:
<div style={{color: ‘red‘}}>
Here‘s an example showcasing some common attributes:
<button className="btn" onClick={handleClick} style={{color: ‘blue‘}}>
Click Me
</button>
JSX component elements can take custom "props" that are passed to the underlying JavaScript function. Props look like attributes but their values are JavaScript expressions:
<Greeting name="John" age={30} />
Here, name and age are props passed to the Greeting component. The curly braces { } around 30 are required to embed the number as a JS expression.
Nesting and Top-Level Elements
JSX allows you to easily nest elements inside one another just like HTML:
<div>
<p>This is a paragraph.</p>
</div>
One quirk of JSX is that each expression must have a single top-level element – you can‘t return two sibling elements side-by-side. If you don‘t want to wrap them in a real DOM element like a <div>, you can use a fragment:
<>
<h2>Sibling 2</h2>
</>
The empty tags <> and </> represent a fragment that doesn‘t render any actual DOM element.
Differences Between JSX and HTML
While JSX looks very similar to HTML, there are a number of important differences to be aware of beyond those already covered with attributes above.
All tags must be explicitly closed, even ones that are self-closing in HTML. For example, an image tag must be written as <img /> instead of just <img>. This is required by the XML-based parser used under the hood by React.
Boolean attributes work slightly differently in JSX than HTML. A boolean prop like disabled will be set to true if you provide the attribute at all, regardless of its actual value. These two are equivalent:
<button disabled>...</button>
<button disabled={true}>...</button>
So explicitly providing a value of false is the only way to make a boolean prop actually false.
How JSX Gets Compiled
As mentioned earlier, JSX is not actually valid JavaScript on its own – it‘s a special syntax that needs to be compiled into regular JavaScript function calls first. The most common tool for this is the Babel compiler, which converts JSX code like this:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
Into this plain JavaScript:
const element = React.createElement(
‘h1‘,
{className: ‘greeting‘},
‘Hello, world!‘
);
The React.createElement function takes three arguments – a tag name string, an object representing props/attributes, and the child elements as variable arguments.
Most React developers never need to write React.createElement calls directly since JSX is so much more readable and maintainable. But it‘s helpful to understand this is what‘s happening under the hood.
Using JavaScript Expressions in JSX
One of the most powerful features of JSX is the ability to embed and evaluate regular JavaScript expressions anywhere inside the markup. As we saw earlier, any JS expression can be used in curly braces { }.
This opens up some very useful possibilities, like conditional rendering:
function Greeting(props) {
return (
<div>
{props.name ? (
) : (
)}
</div>
);
}
Here, the Greeting component will render a different <h1> element depending on whether the name prop was provided or not, using a ternary conditional operator. You can use any JS expression that evaluates to a value, like a function call:
Or mapping an array to JSX elements:
const listItems = numbers.map(number =>
<li key={number.toString()}>
{number}
</li>
);
return <ul>{listItems}</ul>;
This code maps each number in the numbers array to a <li> element, then embeds the array of elements inside a <ul> to render the full list.
A few important limitations to keep in mind:
-
You can‘t use
if/elsestatements,for/whileloops, or variable/function declarations directly inside{ }. Only expressions that evaluate to a value are allowed. For complex logic, extract it into a variable or function first. -
Object literals must be wrapped in an extra set of parentheses like
{{ key: value }}to avoid the curly braces being interpreted as the markup for a code block. -
Spreading props with
<Component {...props} />will not work as expected ifpropscontains any HTML/React-specific attributes likeclassName. Use object destructuring to separate those props from the rest first.
JSX and Props
As we saw in the custom component example earlier, JSX elements can take "props" to customize and configure them, similar to attributes in HTML. The props become properties on the props object passed to the component function.
Props can be static string values, or dynamic JavaScript expressions wrapped in { }. Here‘s an example of passing both:
function Greeting(props) {
return ;
}
const element = <Greeting name="John" age={30} />;
The Greeting component receives an object like {name: ‘John‘, age: 30} as its props argument.
A common best practice is to destructure the props object in the function signature for more readable and less verbose code:
function Greeting({ name, age }) {
return ;
}
Now the function body can refer to name and age directly instead of props.name and props.age.
It‘s important to remember that props should be treated as read-only by the receiving component. A component should never modify its own props directly. If a component needs mutable data that changes over time, that should be managed with React‘s state mechanism instead of props.
Best Practices and Conventions
Here are some tips and conventions to follow for clean, readable, and maintainable JSX code:
- Use multi-line JSX for more complex markup, with each attribute on its own line for better readability:
return (
<div>
<p>This is a paragraph.</p>
</div>
);
-
Wrap multi-line JSX expressions in parentheses
( )to avoid automatic semicolon insertion pitfalls. -
Extract complex conditional rendering logic and nested loops into separate variables before using them in JSX. This keeps the markup section cleaner and easier to read.
-
Avoid fragments
<></>when a wrapper<div>or other element would work just as well. Fragments are great for getting rid of unnecessary markup, but overusing them can make the code harder to follow. -
Keep components small, focused, and reusable. If a component is getting too large or doing too many things, consider breaking it up into smaller sub-components.
Common JSX Mistakes to Avoid
Finally, here are some of the most common mistakes and gotchas to watch out for when writing JSX code:
-
Unclosed Tags: Remember that all elements must be explicitly closed, even ones that are self-closing in HTML like
<img>and<br>. JSX requires<img />and<br />. -
Mismatched Attribute Names: HTML attributes like
classandforare renamed in JSX toclassNameandhtmlForrespectively to avoid clashing with JavaScript reserved words. -
Incorrect Attribute Values: While a JSX attribute can take a string literal or a JavaScript expression, the two can‘t be combined without an explicit
+ ‘‘concatenation. For example,<img src="logo.png" alt={‘Logo for ‘ + siteName} />is valid but<img src="logo.png" alt="Logo for {siteName}" />is not. -
Modifying Props: Props are intended to be read-only snapshots of data passed into a component from its parent. Avoid the temptation to directly modify or reassign the
propsobject in a component – any changes to data should flow back up to the parent component via callbacks instead. -
Inconsistent Returns: A component should always return JSX markup (or
null) regardless of conditionals or early returns. Forgetting to return markup or conditionally returning non-JSX values is a common source of cryptic errors. -
Improper List Keys: When dynamically generating lists of JSX elements from arrays, each element must have a unique
keyprop for efficient rendering and updates. Avoid using array indices as keys when the order of items may change, as that can negatively impact performance. Use unique, stable identifiers for keys whenever possible.
Understanding and applying these key concepts will make you a confident and capable JSX developer in no time. While there‘s certainly more to learn, this guide covers all the fundamentals you need to build robust, readable React applications with the power of JSX. Happy coding!
