JSX (JavaScript XML) âš›ī¸

1. Introduction 👋

JSX (JavaScript XML) is one of the defining features of React development. It allows you to write markup that looks like HTML directly inside your JavaScript code. This tutorial covers everything from basic syntax to advanced rules, common mistakes, and how JSX is compiled behind the scenes.

Information

This tutorial assumes you already have a working React project set up. If not, refer to the Installation & Project Setup tutorial first.

2. What is JSX? 🤔

JSX is a syntax extension for JavaScript that allows you to write HTML-like structures within your JavaScript code. It is not valid JavaScript on its own — it must be compiled into regular JavaScript function calls before it can run in a browser.

A Simple JSX Example

const element = <h1>Hello, world! 👋</h1>;

Behind the scenes, this JSX is transformed into a call to React.createElement() (or a similar function), which produces a plain JavaScript object describing the UI.

>>"JSX produces React elements." — React Documentation

3. Why JSX? 💡

React was designed around the idea that rendering logic and UI markup are inherently coupled — so instead of separating them into different files, JSX lets you keep them together in one cohesive unit: the component.

  • Readability: Markup structure is immediately visible alongside the logic that drives it.
  • Type Safety: With TypeScript, JSX enables compile-time checking of props and elements.
  • Tooling Support: Enables syntax highlighting, autocompletion, and error checking in editors.
  • Familiarity: Its HTML-like syntax makes it approachable for developers with web backgrounds.

Note

JSX is optional in React — you could write React.createElement() calls directly — but virtually all React code in practice uses JSX.

4. JSX Syntax 📐

JSX combines the structure of HTML with the power of JavaScript expressions, all within curly braces {}.

Basic JSX Syntax

const name = "Alice";

const greeting = (
  <div className="greeting">
    <h1>Hello, {name}!</h1>
    <p>Welcome to React.</p>
  </div>
);
  • Tags resemble HTML elements (<div>, <h1>, etc.).
  • Curly braces {} embed JavaScript expressions.
  • Attributes use camelCase instead of HTML's lowercase convention (e.g., className instead of class).

5. Writing Your First JSX âœī¸

App.jsx

function App() {
  const user = "Developer";

  return (
    <div>
      <h1>Hello, {user}! 👋</h1>
      <p>This is your first JSX component.</p>
    </div>
  );
}

export default App;

Tip

Save your file with a .jsx or .tsx extension so your build tool and editor recognize it as containing JSX syntax.

6. JSX Rules 📏

JSX looks like HTML but follows stricter rules since it must ultimately compile into valid JavaScript function calls.

  1. Return a single root element (or a Fragment) from a component.
  2. Close every tag, including self-closing elements like <img /> and <br />.
  3. Use camelCase for most attribute names (e.g., onClick, tabIndex).
  4. Use className instead of class, and htmlFor instead of for.
  5. JavaScript reserved words cannot be used as-is for certain attributes.

Warning

Forgetting to close a tag or returning multiple sibling elements without a wrapper are among the most common JSX compilation errors for beginners.

7. Expressions in JSX 🧮

Any valid JavaScript expression can be embedded inside JSX using curly braces. This includes variables, function calls, arithmetic, ternaries, and more.

Code Snippet

const price = 49.99;
const isDiscounted = true;

const element = (
  <p>
    Price: ${isDiscounted ? (price * 0.9).toFixed(2) : price}
  </p>
);

Caution

Curly braces accept expressions, not statements. You cannot use if, for, or variable declarations directly inside {}.

8. Embedding JavaScript in JSX 🔗

JSX allows seamless embedding of variables, function calls, and even other JSX elements.

Code Snippet

function formatDate(date) {
  return date.toLocaleDateString();
}

function Article({ title, publishedAt }) {
  return (
    <article>
      <h2>{title}</h2>
      <p>Published on {formatDate(publishedAt)}</p>
    </article>
  );
}
Expression TypeExample
Variables{userName}
Function Calls{formatDate(date)}
Arithmetic{price * quantity}
Ternary Expressions{isActive ? "Yes" : "No"}

9. JSX Attributes đŸˇī¸

JSX attributes behave like HTML attributes but accept JavaScript expressions as values when wrapped in curly braces, instead of only string literals.

Code Snippet

const imageUrl = "https://example.com/photo.jpg";
const isDisabled = false;

const element = (
  <div>
    <img src={imageUrl} alt="Profile photo" width={200} />
    <button disabled={isDisabled}>Submit</button>
  </div>
);
  • String values use quotes: title="Hello".
  • Expression values use curly braces: title={variable}.
  • Boolean attributes can be shorthand: <input disabled /> implies disabled={true}.

10. HTML vs JSX âš”ī¸

AspectHTMLJSX
Class Attributeclass="box"className="box"
Label Forfor="name"htmlFor="name"
Inline StylesString: style="color:red"Object: style={{ color: "red" }}
Event AttributesLowercase: onclickcamelCase: onClick
Self-Closing TagsOptional: <img>Required: <img />
Comments<!-- comment -->{/* comment */}

11. JSX Children đŸ‘ļ

Elements in JSX can contain children — nested elements, text, expressions, or a combination of all three.

Code Snippet

const element = (
  <div>
    Plain text child
    <span>An element child</span>
    {"An expression child"}
    <p>Multiple <strong>nested</strong> children</p>
  </div>
);

Note

Children can also be functions, arrays, or other components — React normalizes and renders them accordingly.

12. Self-Closing Elements ⭕

Elements without children must be self-closed in JSX, unlike in HTML where this is often optional.

Code Snippet

// Correct
const good = (
  <div>
    <img src="photo.jpg" alt="A photo" />
    <input type="text" />
    <br />
    <hr />
  </div>
);

// Incorrect — will cause a compile error
// const bad = <img src="photo.jpg">;

13. Nested Elements đŸĒ†

JSX supports arbitrarily deep nesting, just like HTML, allowing you to compose complex UI structures from simple elements.

Code Snippet

const card = (
  <div className="card">
    <div className="card-header">
      <h2>Card Title</h2>
    </div>
    <div className="card-body">
      <p>Some <em>nested</em> content goes here.</p>
      <ul>
        <li>Item one</li>
        <li>Item two</li>
      </ul>
    </div>
  </div>
);

Tip

Deeply nested JSX can hurt readability — consider extracting nested sections into their own components once nesting grows beyond 3–4 levels.

14. Wrapping Multiple Elements đŸ“Ļ

A component must return a single root element. To return multiple sibling elements, wrap them in a parent element or a Fragment.

Code Snippet

// ❌ Invalid — multiple root elements
// function Invalid() {
//   return (
//     <h1>Title</h1>
//     <p>Description</p>
//   );
// }

// ✅ Valid — wrapped in a single parent
function Valid() {
  return (
    <div>
      <h1>Title</h1>
      <p>Description</p>
    </div>
  );
}

15. React Fragments 🧩

Fragments let you group multiple elements without introducing an extra DOM node — useful when you don't want an unnecessary wrapping <div>.

Code Snippet

import { Fragment } from 'react';

function List() {
  return (
    <Fragment>
      <li>First item</li>
      <li>Second item</li>
    </Fragment>
  );
}

// Shorthand syntax
function ListShorthand() {
  return (
    <>
      <li>First item</li>
      <li>Second item</li>
    </>
  );
}

Tip

Use the shorthand <> </> syntax unless you need to pass a key prop, which requires the explicit <Fragment> form.

16. Comments in JSX đŸ’Ŧ

Comments inside JSX markup must be written as JavaScript expressions wrapped in curly braces, using the standard /* */ block comment syntax.

Code Snippet

function App() {
  return (
    <div>
      {/* This is a comment inside JSX */}
      <h1>Hello!</h1>
      {
        // Multi-line comments work too,
        // but block comments are more common
      }
    </div>
  );
}

Caution

Standard HTML-style comments (<!-- -->) and plain // comments outside curly braces are not valid inside JSX markup.

17. Conditional Rendering in JSX 🔀

JSX doesn't support if statements directly inside markup, but several JavaScript patterns achieve conditional rendering elegantly.

Code Snippet

function Status({ isOnline }) {
  return <p>{isOnline ? "đŸŸĸ Online" : "🔴 Offline"}</p>;
}

Code Snippet

function Notification({ hasUnread }) {
  return (
    <div>
      {hasUnread && <span className="badge">New</span>}
    </div>
  );
}

Code Snippet

function Profile({ user }) {
  if (!user) {
    return <p>Loading...</p>;
  }
  return <h1>Welcome, {user.name}!</h1>;
}

Warning

Be careful using && with numeric values — {0 && <Component />} renders the number 0 instead of nothing, since 0 is falsy but still gets rendered.

18. Rendering Lists with JSX 📋

Arrays of data can be transformed into lists of elements using JavaScript's .map() method.

Code Snippet

const fruits = ["Apple", "Banana", "Cherry"];

function FruitList() {
  return (
    <ul>
      {fruits.map((fruit) => (
        <li key={fruit}>{fruit}</li>
      ))}
    </ul>
  );
}

19. Keys in JSX 🔑

When rendering lists, each element needs a unique key prop so React can efficiently track, reorder, and update items during reconciliation.

Code Snippet

const users = [
  { id: "u1", name: "Alice" },
  { id: "u2", name: "Bob" },
];

function UserList() {
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Important

Prefer a stable, unique identifier (like a database ID) over the array index as a key — index-based keys can cause subtle bugs when items are reordered, added, or removed.

20. Event Handling in JSX đŸ–ąī¸

JSX handles events using camelCase attribute names and passes a function reference rather than a string, as opposed to HTML's inline onclick="..." style.

Code Snippet

function Button() {
  function handleClick() {
    console.log("Button clicked!");
  }

  return <button onClick={handleClick}>Click Me</button>;
}

// Passing arguments with an arrow function
function DeleteButton({ id, onDelete }) {
  return (
    <button onClick={() => onDelete(id)}>Delete</button>
  );
}

Caution

Avoid calling the function directly, e.g. onClick={handleClick()} — this executes it immediately during render instead of on click.

21. Inline Styles in JSX 🎨

Inline styles in JSX use a JavaScript object instead of a CSS string, with property names written in camelCase.

Code Snippet

function Alert() {
  const alertStyle = {
    backgroundColor: "#fee2e2",
    color: "#991b1b",
    padding: "12px",
    borderRadius: "8px",
  };

  return <div style={alertStyle}>Something went wrong!</div>;
}

Note

Numeric values are treated as px by default for most properties (e.g., fontSize: 16 becomes 16px), except for unitless properties like opacity.

22. Class Names in JSX đŸˇī¸

Since class is a reserved word in JavaScript, JSX uses className to apply CSS classes.

Code Snippet

function Card({ isActive }) {
  return (
    <div className={isActive ? "card card-active" : "card"}>
      Card content
    </div>
  );
}

Tip

For conditional class names, utility libraries like clsx or classnames can simplify complex class logic significantly.

23. Special JSX Attributes 🌟

AttributePurpose
keyHelps React identify list items during reconciliation.
refProvides direct access to a DOM node or component instance.
dangerouslySetInnerHTMLInjects raw HTML directly, bypassing React's escaping.
childrenRepresents nested content passed between opening and closing tags.

Danger

Use dangerouslySetInnerHTML with extreme caution — injecting unsanitized content can expose your app to XSS attacks.

24. Escaping Values in JSX đŸ›Ąī¸

React automatically escapes values embedded in JSX before rendering them, protecting your application against XSS injection attacks by default.

Code Snippet

const userInput = "<script>alert('hacked')</script>";

// Safe — React escapes this automatically, rendering it as plain text
const element = <p>{userInput}</p>;

Success

This automatic escaping is one of React's built-in security benefits — you rarely need to manually sanitize text content rendered through standard JSX expressions.

25. JSX Compilation âš™ī¸

Browsers cannot interpret JSX natively. A compiler such as Babel or SWC transforms JSX into standard JavaScript function calls before the code ever reaches the browser.

Before Compilation

const element = <h1 className="title">Hello!</h1>;

After Compilation (Classic Runtime)

const element = React.createElement(
  "h1",
  { className: "title" },
  "Hello!"
);

26. JSX Transform 🔄

React introduced a new JSX transform in React 17 that removes the need to manually import React in every file that uses JSX.

Code Snippet

import React from 'react';

const element = React.createElement("h1", null, "Hello!");

Code Snippet

import { jsx as _jsx } from 'react/jsx-runtime';

const element = _jsx("h1", { children: "Hello!" });

Information

Modern tools like Vite and Next.js use the automatic transform by default, so you no longer need import React from 'react' just to use JSX.

27. JSX Best Practices 🌟

  1. Keep JSX readable by extracting complex logic into variables or helper functions before the return statement.
  2. Always provide a stable, unique key when rendering lists.
  3. Prefer Fragment over unnecessary wrapper <div> elements.
  4. Break large components into smaller, focused ones instead of deeply nesting JSX.
  5. Use self-closing tags consistently for elements without children.
  6. Avoid inline object/array literals in props when they cause unnecessary re-renders in performance-sensitive components.

28. Common JSX Mistakes đŸšĢ

  • Forgetting to close self-closing tags like <img> or <input>.
  • Returning multiple root elements without a wrapping Fragment or parent element.
  • Using class instead of className.
  • Passing a string to style instead of an object.
  • Using array index as a key in dynamic lists.
  • Calling event handlers immediately, e.g. onClick={handleClick()}, instead of passing a reference.

Danger

Rendering {0 && <Component />} unexpectedly displays the number 0 on screen — use an explicit boolean conversion like {Boolean(count) && <Component />} to avoid this.

29. Frequently Asked Questions ❓

Question

Is JSX required to use React?

Answer

No, JSX is optional. You can write React applications using React.createElement() directly, but JSX is overwhelmingly preferred for its readability.

Question

Can I use JSX outside of React?

Answer

Yes. JSX is a general-purpose syntax extension. Other libraries, such as Preact or SolidJS, also support JSX with their own compilation targets.

Question

Why does JSX require a single root element?

Answer

Because JSX compiles to a single function call representing one element tree — Fragment exists specifically to satisfy this requirement without adding extra DOM nodes.

30. Summary 📝

JSX bridges the gap between JavaScript logic and UI markup, offering a readable, expressive syntax that compiles down to plain JavaScript function calls. Understanding its rules — from self-closing tags and Fragments to keys and event handling — is essential for writing clean, maintainable React components.

Summary

Mastering JSX lays the groundwork for everything else in React, including components, props, and state, which build directly on the syntax and concepts covered here.