
JSX (JavaScript XML) is one of React’s most distinctive features and a cornerstone of writing React applications. It allows you to write HTML-like code directly within your JavaScript, providing a more intuitive way to describe UI components. This chapter will help you understanding JSX, its syntax, and how it works behind the scenes.
What is JSX?
JSX is a syntax extension for JavaScript that looks similar to HTML or XML. It was developed by Facebook specifically for React as a way to define what the UI should look like. While it resembles HTML, JSX is actually closer to JavaScript.
JSX Example
const element = <h1 className="greeting">Hello, world!</h1>;
This might look like HTML at first glance, but it’s actually JSX. When your application is built, this JSX is transformed into regular JavaScript function calls.
JSX vs HTML: Key Differences
While JSX looks like HTML, there are several important differences:
- Attributes use camelCase: In JSX, HTML attributes are written in camelCase rather than kebab-case.
// HTML: <div class="container" tabindex="0"></div>
// JSX:
<div className="container" tabIndex="0"></div>
- className instead of class: Since class is a reserved keyword in JavaScript, JSX uses className instead.
<div className="container"></div>
- Self-closing tags require a slash: In JSX, self-closing tags must include a closing slash.
// HTML: <img src="image.jpg">
// JSX:
<img src="image.jpg" />
- JavaScript expressions in curly braces: JSX allows you to embed JavaScript expressions by wrapping them in curly braces.
const name = 'John';
const element = <h1>Hello, {name}!</h1>;
How JSX Works Behind the Scenes
When you write JSX, it gets transformed into regular JavaScript code by tools like Babel before being executed by the browser. Let’s look at how this transformation works:
JSX Transformation
This JSX:
const element = <h1 className="greeting">Hello, world!</h1>;
Gets transformed into:
const element = React.createElement(
 'h1',
 { className: 'greeting' },
 'Hello, world!'
);
The React.createElement() function creates a JavaScript object called a “React element” that describes what should appear on the screen. This is why we need to import React at the top of files that use JSX, even if React isn’t directly referenced in the code.
The Structure of React Elements
When React.createElement() is called, it produces an object that looks something like this:
// Simplified version of what React.createElement() returns
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
React uses these objects to build and maintain the DOM.
Writing JSX
Let’s explore how to write JSX code for different scenarios:
Basic JSX Syntax
JSX elements can be simple:
const element = <div>Hello, world!</div>;
Or they can contain multiple nested elements. When using multiple lines, wrap the JSX in parentheses to avoid automatic semicolon insertion issues:
const element = (
 <div>
  <h1>Hello!</h1>
  <p>This is a paragraph.</p>
 </div>
);
Root Element Requirement
JSX expressions must have exactly one root element. To return multiple elements without an extra wrapper, use React Fragments:
// Using explicit Fragment syntax
const element = (
<React.Fragment>
<h1>Title</h1>
<p>Paragraph</p>
</React.Fragment>
);
// Using short syntax (empty tags)
const element = (
<>
<h1>Title</h1>
<p>Paragraph</p>
</>
);
JavaScript Expressions in JSX
You can embed any JavaScript expression in JSX by wrapping it in curly braces {}:
const name = 'John';
const element = <h1>Hello, {name}!</h1>;
// You can use expressions in attributes too
const imageUrl = 'https://example.com/image.jpg';
const imgElement = <img src={imageUrl} alt="Example" />;
// You can even call functions
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = { firstName: 'John', lastName: 'Doe' };
const greeting = <h1>Hello, {formatName(user)}!</h1>;
JSX is an Expression Too
Since JSX compiles into JavaScript function calls, you can use JSX wherever you can use expressions:
// In conditionals
function getGreeting(user) {
if (user) {
return <h1>Hello, {user.name}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
// In variables
const userGreeting = <h1>Welcome back!</h1>;
const guestGreeting = <h1>Please sign up.</h1>;
// In function returns
function Greeting(props) {
if (props.isLoggedIn) {
return userGreeting;
}
return guestGreeting;
}
Multiline JSX
For readability, multiline JSX should be wrapped in parentheses:
const element = (
 <div>
  <h1>Hello!</h1>
  <p>Welcome to JSX.</p>
 </div>
);
JSX Attributes
JSX attributes are written using camelCase convention and can accept string literals or JavaScript expressions.
String Literals
const element = <div className="container">Hello</div>;
JavaScript Expressions
const isActive = true;
const element = <div className={isActive ? 'active' : 'inactive'}>Item</div>;
Spread Attributes
You can use the spread operator to pass an entire props object:
const props = { firstName: 'John', lastName: 'Doe' };
const element = <Greeting {...props} />;
Boolean Attributes
For boolean attributes, including just the name implies true:
// These are equivalent:
<input disabled={true} />
<input disabled />
JSX Children
JSX elements can contain child elements, just like HTML:
const element = (
 <div>
  <h1>Title</h1>
  <p>Paragraph</p>
 </div>
);
String Literals as Children
const element = <div>Hello, world!</div>;
JavaScript Expressions as Children
const element = <div>{2 + 2}</div>; // Renders "4"
Functions as Children
function Item(props) {
return <li>{props.message}</li>;
}
function List() {
const items = ['Apple', 'Banana', 'Cherry'];
return (
<ul>
{items.map((item, index) => (
<Item key={index} message={item} />
))}
</ul>
);
}
Conditional Rendering in JSX
There are several ways to render content conditionally in JSX:
If Statements (Outside JSX)
function Greeting(props) {
 if (props.isLoggedIn) {
  return <UserGreeting />;
 }
 return <GuestGreeting />;
}
Inline Logical && Operator
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
Ternary Operator
function UserStatus(props) {
 return (
  <div>
   The user is {props.isLoggedIn ? 'logged in' : 'logged out'}.
  </div>
 );
Preventing Component Rendering
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
Lists and Keys in JSX
When rendering lists in React, you need to provide a unique “key” for each item:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) => (
<li key={number.toString()}>
{number}
</li>
))}
</ul>
);
}
Rules for Keys
- Keys must be unique among siblings (not globally)
- Use stable IDs when available (e.g., database IDs)
- Use indices as a last resort (can cause issues with reordering)
// Good (unique IDs):
{items.map(item => <ListItem key={item.id} item={item} />)}
// Less ideal (indices):
{items.map((item, index) => <ListItem key={index} item={item} />)}
JSX with CSS
There are several ways to include CSS with JSX:
1. CSS Classes
import './Button.css'; // Import the CSS file
function Button() {
return <button className="primary-button">Click Me</button>;
}import './Button.css'; // Import the CSS file
function Button() {
return <button className="primary-button">Click Me</button>;
}
2. Inline Styles
Inline styles in JSX use JavaScript objects with camelCase properties:
function Button() {
 const buttonStyle = {
  backgroundColor: 'blue',
  color: 'white',
  padding: '10px 15px',
  borderRadius: '4px',
  border: 'none',
  cursor: 'pointer'
 };
 return <button style={buttonStyle}>Click Me</button>
 }
Or inline:
function Button() {
 return (
  <button
   style={{
    backgroundColor: 'blue',
    color: 'white'
   }}
  >
   Click Me
  </button>
 );
}
3. CSS Modules
CSS Modules scope CSS locally by default:
/* Button.module.css */
.button {
background-color: blue;
color: white;
}
import styles from './Button.module.css';
function Button() {
return <button className={styles.button}>Click Me</button>;
}
4. Styled Components
Using a library like styled-components:
import styled from 'styled-components';
const StyledButton = styled.button`
background-color: blue;
color: white;
padding: 10px 15px;
border-radius: 4px;
border: none;
cursor: pointer;
`;
function Button() {
return <StyledButton>Click Me</StyledButton>;
}
JSX Gotchas and Best Practices
1. Comments in JSX
JSX comments must be wrapped in curly braces:
const element = (
 <div>
  {/* This is a comment in JSX */}
  <h1>Hello, world!</h1>
 </div>
);
2. HTML Entities
HTML entities can be used directly in JSX text:
const element = <div>First· Second</div>;
Or via Unicode characters:
const element = <div>First · Second</div>;
3. Avoiding XSS with JSX
React automatically escapes values embedded in JSX to prevent cross-site scripting (XSS) attacks:
const userInput = '<script>alert("XSS attack!")</script>';
// This is safe, React will escape the content
const element = <div>{userInput}</div>;
This renders the string as text, not as HTML.
4. Dangerous HTML Rendering
In rare cases when you need to render HTML content, you can use dangerouslySetInnerHTML (but use with caution!):
function createMarkup() {
 return {__html: '<div>HTML content</div>'};
}
function MyComponent() {
 return <div dangerouslySetInnerHTML={createMarkup()} />;
}
5. Custom HTML Attributes
For non-standard HTML attributes, use the data- prefix:
<div data-custom-attribute="value" />
Or for completely custom components, any prop name is valid:
<MyComponent customAttribute="value" />
JSX Alternatives
While JSX is the preferred way to write React components, it’s not required:
Using React Without JSX
// With JSX:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
// Without JSX (equivalent code):
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
Advanced JSX Patterns
1. Dynamic Tag Names
You can use variables for component or element types:
function Story(props) {
 // Capitalize variable names for components
 const SpecificStory = components[props.storyType];
 return <SpecificStory story={props.story} />;
}
2. Render Props Pattern
function MouseTracker() {
 return (
  <Mouse render={mouse => (
   <p>The mouse position is {mouse.x}, {mouse.y}</p>
  )}/>
 );
}
3. JSX Spread Attributes with Exclusion
function Button({ className, ...props }) {
 return (
  <button
   className={`default-button ${className}`}
   {...props}
  />
 );
}
Practical JSX Examples
1. A Form Component
function ContactForm() {
const [name, setName] = React.useState('');
const [email, setEmail] = React.useState('');
const [message, setMessage] = React.useState('');
const handleSubmit = (e) => {
e.preventDefault();
console.log({ name, email, message });
};
return (
<form onSubmit={handleSubmit} className="contact-form">
<div className="form-group">
<label htmlFor="name">Name:</label>
<input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
</div>
<div className="form-group">
<label htmlFor="email">Email:</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div className="form-group">
<label htmlFor="message">Message:</label>
<textarea
id="message"
value={message}
onChange={(e) => setMessage(e.target.value)}
required
rows="5"
></textarea>
</div>
<button type="submit" className="submit-button">
Send Message
</button>
</form>
);
}
2. A Card Component
function Card({ title, description, imageUrl, tags = [] }) {
return (
<div className="card">
{imageUrl && (
<div className="card-image">
<img src={imageUrl} alt={title} />
</div>
)}
<div className="card-content">
<h2 className="card-title">{title}</h2>
<p className="card-description">{description}</p>
{tags.length > 0 && (
<div className="card-tags">
{tags.map((tag, index) => (
<span key={index} className="tag">
{tag}
</span>
))}
</div>
)}
</div>
</div>
);
}
3. A Navigation Menu
function Navigation({ links, activeLink }) {
return (
<nav className="main-navigation">
<ul className="nav-list">
{links.map((link) => (
<li key={link.id} className="nav-item">
<a
href={link.url}
className={`nav-link ${activeLink === link.id ? 'active' : ''}`}
>
{link.icon && <span className="nav-icon">{link.icon}</span>}
<span className="nav-text">{link.text}</span>
</a>
</li>
))}
</ul>
</nav>
);
}
Debugging JSX
When working with JSX, common errors include:
- Missing closing tags: All JSX elements must be closed.
- Case sensitivity: JSX components must start with a capital letter.
- Runtime errors: Check the console for detailed error messages.
- Incorrect component imports: Ensure you’re importing all needed components.
Using React Developer Tools
The React Developer Tools browser extension helps with debugging JSX:
- Inspect component hierarchy
- View props and state of each component
- Track component re-renders
- Debug performance issues
Summary
JSX is a powerful syntax extension for JavaScript that makes React code more readable and intuitive. In this chapter, we’ve covered:
- What JSX is and how it differs from HTML
- How JSX works behind the scenes with React.createElement()
- Basic JSX syntax rules and patterns
- Working with expressions, attributes, and children in JSX
- Conditional rendering patterns
- List rendering and the importance of keys
- Different ways to include CSS with JSX
- Common gotchas and best practices
- Advanced JSX patterns and practical examples
JSX bridges the gap between JavaScript logic and UI, allowing you to write more expressive React components. Though it might seem like a syntax oddity at first, JSX quickly becomes natural and preferable for React development.
In the next chapter, we’ll dive into React Components, building on our understanding of JSX to create reusable UI building blocks.
Further Reading
- JSX in Depth (React Docs)
- Introducing JSX (React Docs)
- Babel JavaScript Compiler – Try converting JSX to JavaScript
- React Without JSX