JSX

React · Lesson 3 of 29 7 min read Updated

JSX lets you write HTML-like tags directly inside JavaScript. It is the reason React code is easy to read: you can see the shape of the page right next to the logic that fills it with data.

In this lesson you will learn what JSX really is, the few rules that make it different from HTML, how to put JavaScript values inside it with curly braces, and what the browser finally receives after JSX is compiled.

JSX is JavaScript in disguise

JSX stands for JavaScript XML. It is a syntax extension: browsers do not understand it, so a tool (Vite in your project, or this site's editor) compiles it to normal JavaScript function calls before the code runs.

React JSX
const heading = <h1 className="title">Namaste, Aarav</h1>;

After compiling, the line above becomes roughly this:

JavaScript
import { jsx } from "react/jsx-runtime";

const heading = jsx("h1", { className: "title", children: "Namaste, Aarav" });

The jsx() call returns a plain JavaScript object that describes an <h1> element. It does not create a real DOM element. React reads these description objects later and builds or updates the real page. You can prove that JSX is just an object:

React JSXTry it yourself
const heading = <h1 className="title">Namaste, Aarav</h1>;

console.log(typeof heading);
console.log(heading.type);
console.log(JSON.stringify(heading.props));
Output
object
h1
{"className":"title","children":"Namaste, Aarav"}

Because JSX produces values, you can store it in variables, return it from functions, put it in arrays and pass it to other functions, exactly like any other value.

Curly braces: JavaScript inside JSX

Inside JSX, curly braces { } open a window back into JavaScript. Anything that produces a value can go there: a variable, a calculation, a function call or a ternary. You can use braces for the text between tags and for attribute values.

React JSXTry it yourself
import { createRoot } from "react-dom/client";

const student = {
  name: "Isha Verma",
  rollNo: 42,
  city: "Lucknow",
  marks: [78, 91, 85],
  photo: "https://course.ethicscomputer.in/assets/samples/avatar.svg",
};

function IdCard() {
  const total = student.marks.reduce((sum, m) => sum + m, 0);
  const average = Math.round(total / student.marks.length);

  return (
    <div className="card">
      <img src={student.photo} alt={`Photo of ${student.name}`} width="80" />
      <h2>{student.name.toUpperCase()}</h2>
      <p>Roll number: {student.rollNo}, {student.city}</p>
      <p>Average marks: {average} ({average >= 80 ? "Distinction" : "Pass"})</p>
    </div>
  );
}

createRoot(document.getElementById("root")).render(<IdCard />);

Notice the attribute src={student.photo}: no quotes around the braces. Use quotes for fixed text (width="80") and braces for JavaScript values. A template literal inside braces builds the alt text from data.

Braces accept expressions, not statements. You cannot write if, for or const inside them. Use a ternary (a ? b : c) or compute the value above the return, as average is computed here. You will learn more patterns in Conditional Rendering.

The rules that differ from HTML

JSX is stricter than HTML and uses JavaScript naming in a few places:

  • Return one root element. A component returns one value, so wrap sibling elements in a parent. If you do not want an extra <div> on the page, use a fragment: <> and </>.
  • Close every tag. Empty elements need a slash: <img />, <br />, <input />.
  • Use camelCase attribute names. class becomes className (because class is a JavaScript keyword), for becomes htmlFor, tabindex becomes tabIndex, and events are onClick, onChange and so on. Attributes starting with aria- and data- keep their dashes.
  • style takes an object, not a string. Write style={{ color: "teal", fontSize: 18 }}. The outer braces open JavaScript; the inner braces are the object. CSS property names are camelCase, and plain numbers mean pixels for most properties.
  • Comments go inside braces: {/* like this */}.

Here is a small HTML form converted to JSX with all these rules applied:

React JSXTry it yourself
import { createRoot } from "react-dom/client";

function Subscribe() {
  return (
    <>
      {/* A fragment: no extra div is added to the page */}
      <h2 style={{ color: "#087ea4", marginBottom: 4 }}>Train alerts</h2>
      <p>Get platform changes for the Jaipur Express.</p>
      <label htmlFor="phone">Mobile number</label>
      <br />
      <input id="phone" type="tel" placeholder="98765 43210" aria-describedby="hint" />
      <p id="hint" className="hint">We send one SMS per journey.</p>
    </>
  );
}

createRoot(document.getElementById("root")).render(<Subscribe />);

What JSX shows and what it ignores

Strings and numbers between tags appear as text. The values true, false, null and undefined render nothing, which is handy for showing something only sometimes. Arrays of elements render one after another. A plain object, however, is not valid as a child, and React throws an error if you try to render one.

React JSXTry it yourself
import { createRoot } from "react-dom/client";

const order = { item: "Masala dosa", qty: 2 };

function Demo() {
  return (
    <ul>
      <li>Number: {42}</li>
      <li>Boolean: {true} (nothing shown)</li>
      <li>Null: {null} (nothing shown)</li>
      <li>Zero: {0} (zero is shown!)</li>
      <li>Object field: {order.item} x {order.qty}</li>
      {/* <li>{order}</li> would throw: objects are not valid as a React child */}
    </ul>
  );
}

createRoot(document.getElementById("root")).render(<Demo />);

JSX also escapes every value you put in braces. If a user types <script> into a comment box and you render {comment}, React shows the characters as text instead of running them. This protects your app from cross-site scripting (XSS) attacks. React has an escape hatch named dangerouslySetInnerHTML for inserting raw HTML; as the name says, avoid it unless the HTML comes from a source you fully trust.

Common mistakes

  • Writing class instead of className. It is a common habit from HTML; React warns about it in development.
  • Two root elements. return <h1>Hi</h1><p>Bye</p>; is a syntax error. Wrap them in <>...</>.
  • Quotes around braces. src="{photo}" passes the literal text {photo}. Write src={photo}.
  • A style string. style="color: red" fails; write style={{ color: "red" }}.
  • Showing 0 by accident. {count && <p>...</p>} prints 0 when count is zero, because 0 is a number and numbers render.

Key points

  • JSX is a syntax extension that compiles to jsx() calls returning plain description objects.
  • Curly braces put any JavaScript expression into JSX, in text or in attributes.
  • Return a single root element; use a fragment <>...</> to avoid extra wrappers.
  • Use className, htmlFor, camelCase events and a style object.
  • true, false, null and undefined render nothing; 0 and other numbers render.
  • React escapes values in braces, which protects against XSS.

Exercise

Write a component TrainTicket that stores a passenger name, train name, from and to cities, coach and seat in an object. Render them in JSX with a heading for the train, a line "From Rohtak to Jaipur", and the seat as "Coach B2, Seat 34". Give the outer element a CSS class ticket and a light border using a style object.

Show solution
React JSX
import { createRoot } from "react-dom/client";

const ticket = { passenger: "Kabir Singh", train: "Intercity Express", from: "Rohtak", to: "Jaipur", coach: "B2", seat: 34 };

function TrainTicket() {
  return (
    <div className="ticket" style={{ border: "1px solid #ccc", padding: 12, borderRadius: 8 }}>
      <h2>{ticket.train}</h2>
      <p>Passenger: {ticket.passenger}</p>
      <p>From {ticket.from} to {ticket.to}</p>
      <p>Coach {ticket.coach}, Seat {ticket.seat}</p>
    </div>
  );
}

createRoot(document.getElementById("root")).render(<TrainTicket />);

Every value comes from the object through curly braces, the class uses className, and the style attribute receives an object with camelCase property names.

Quick Quiz

1. What does a JSX element such as <h1>Hi</h1> produce when the code runs?

2. Which line is valid JSX?

3. What does {false} render inside JSX?

4. Why must return <h1>A</h1><p>B</p>; be changed?

Learn React with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call