React SME Cookbook
All FAQs

Search Documentation

Search across all documentation pages

jsxtsxsyntaxtypescriptfragments

JSX and TSX

Write declarative UI using JavaScript expressions that look like HTML — with full TypeScript support.

Recipe

Quick-reference recipe card — copy-paste ready.

// JSX expression
const element = <h1 className="title">Hello, {name}</h1>
 
// Fragment (no extra DOM node)
const list = (
  <>
    <dt>Term</dt>
    <dd>Definition</dd>
  </>
)
 
// Expression slot — any valid JS expression works inside { }
<p>{isLoggedIn ? `Welcome, ${user.name}` : "Please sign in"}</p>
 
// Spread props
<Button {...props} />

When to reach for this: Every React component returns JSX. Understanding the rules saves hours of debugging.

Working Example

"use client";
 
import { useState } from "react";
 
interface UserCardProps {
  name: string;
  role: "admin" | "editor" | "viewer";
  avatarUrl?: string;
}
 
export function UserCard({ name, role, avatarUrl }: UserCardProps) {
  const [expanded, setExpanded] = useState(false);
 
  const roleBadge: Record<string, string> = {
    admin: "bg-red-100 text-red-800",
    editor: "bg-blue-100 text-blue-800",
    viewer: "bg-gray-100 text-gray-800",
  };
 
  return (
    <article className="rounded-lg border p-4">
      <div className="flex items-center gap-3">
        {avatarUrl ? (
          <img
            src={avatarUrl}
            alt={`${name}'s avatar`}
            className="h-10 w-10 rounded-full"
          />
        ) : (
          <div className="flex h-10 w-10 items-center justify-center rounded-full bg-gray-200">
            {name[0]}
          </div>
        )}
        <div>
          <h2 className="font-semibold">{name}</h2>
          <span className={`rounded px-2 py-0.5 text-xs ${roleBadge[role]}`}>
            {role}
          </span>
        </div>
      </div>
      {expanded && (
        <p className="mt-3 text-sm text-gray-600">
          {name} has <strong>{role}</strong> permissions in this workspace.
        </p>
      )}
      <button
        onClick={() => setExpanded(prev => !prev)}
        className="mt-2 text-sm text-blue-600 underline"
      >
        {expanded ? "Show less" : "Show more"}
      </button>
    </article>
  );
}

What this demonstrates:

  • Conditional rendering with ternary expressions inside JSX
  • Dynamic className using template literals
  • Expression slots for computed values
  • TypeScript interface for component props

Deep Dive

How It Works

  • JSX is syntactic sugar — <div className="x"> compiles to React.createElement("div", { className: "x" }) (or the modern JSX transform which imports jsx from react/jsx-runtime automatically)
  • Every JSX expression must return a single root element — use <>...</> (Fragment) when you don't want an extra wrapper in the DOM
  • Curly braces { } create an expression slot — anything that produces a value works: variables, ternaries, function calls, array methods
  • TSX is identical to JSX but lives in .tsx files and supports TypeScript type-checking for props, expressions, and event handlers

JSX Rules at a Glance

RuleCorrectWrong
HTML classclassNameclass
HTML forhtmlForfor
Self-closing tags<img /><img>
Style propstyle={{ color: "red" }}style="color:red"
Boolean attributes<input disabled /><input disabled="true">
Data attributesdata-testid="x"Works as-is

Fragments

// Named fragment (when you need a key)
import { Fragment } from "react";
 
{items.map(item => (
  <Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.definition}</dd>
  </Fragment>
))}
 
// Short syntax (no key needed)
<>
  <Header />
  <Main />
  <Footer />
</>

TypeScript Notes

// React.ReactNode — the broadest return type
function Wrapper({ children }: { children: React.ReactNode }) {
  return <section>{children}</section>;
}
 
// React.JSX.Element — a single JSX element
function Badge(): React.JSX.Element {
  return <span>OK</span>;
}
 
// Generic components
function List<T>({ items, render }: { items: T[]; render: (item: T) => React.ReactNode }) {
  return <ul>{items.map((item, i) => <li key={i}>{render(item)}</li>)}</ul>;
}

Gotchas

  • class vs className — Using class in JSX compiles but triggers a console warning and won't apply styles correctly in some environments. Fix: Always use className.

  • Adjacent elements without a wrapper — Returning <h1/><p/> without a parent element is a syntax error. Fix: Wrap in a Fragment <>...</> or a <div>.

  • Rendering objects{user} throws "Objects are not valid as a React child" if user is an object. Fix: Render a specific property like {user.name}.

  • Whitespace collapsing — JSX collapses whitespace more aggressively than HTML. Two inline elements like <span>A</span><span>B</span> will have no space between them. Fix: Add {" "} between them or wrap with CSS gap.

  • Comments in JSX — HTML comments <!-- --> don't work. Fix: Use {/* comment */} inside JSX.

Alternatives

AlternativeUse WhenDon't Use When
Template literals (tagged)Building email HTML or static strings server-sideYou need interactivity or React features
React.createElementProgrammatically building elements in a loop without JSXReadability matters (JSX is almost always clearer)
MDXWriting content-heavy pages that mix Markdown and componentsBuilding interactive app UI

FAQs

What is the difference between JSX and TSX?

TSX is JSX inside .tsx files with TypeScript type-checking enabled. The syntax is identical, but TSX validates prop types, expression types, and event handler types at compile time.

Why do I have to use className instead of class?

class is a reserved keyword in JavaScript. JSX compiles to JavaScript function calls, so React uses className to avoid the conflict. Using class triggers a console warning.

What can go inside curly braces in JSX?

Any valid JavaScript expression — variables, ternaries, function calls, template literals, and array methods like .map(). Statements like if/else or for loops cannot go directly inside {}.

When should I use a Fragment instead of a div?

Use <>...</> when you need to return multiple sibling elements without adding an extra DOM node. Use <Fragment key={...}> when mapping a list that needs keys on the wrapper.

How does the style prop work in JSX?

The style prop accepts a JavaScript object with camelCase properties, not a CSS string:

<div style={{ backgroundColor: "red", fontSize: 16 }} />
Can I use if/else inside JSX?

Not directly. Use a ternary expression {condition ? <A /> : <B />} or extract the logic into a variable before the return statement.

What does the spread operator do on a JSX element?

<Button {...props} /> passes all properties of the props object as individual attributes to the component. This is useful for forwarding props or extending native HTML elements.

How do I write comments inside JSX?

Use {/* comment */} inside JSX. HTML-style <!-- --> comments do not work and will cause a syntax error.

What is React.ReactNode vs React.JSX.Element?
  • React.ReactNode is the broadest type — includes JSX, strings, numbers, null, undefined, and arrays
  • React.JSX.Element is a single JSX element only, no strings or null
  • Use ReactNode for children props and JSX.Element for function return types that always return JSX
Why does JSX require a single root element?

JSX expressions compile to a single function call like React.createElement(...). Multiple adjacent elements would be multiple return values, which JavaScript doesn't support. Wrap siblings in a Fragment or a container element.

How do I render raw HTML strings in JSX?

Use dangerouslySetInnerHTML={{ __html: htmlString }}. Only use this with sanitized, trusted content — it bypasses React's XSS protections.

What happens when I render false, null, or undefined in JSX?

They render nothing. This is why {condition && <Component />} works — when condition is false, false renders as empty. However, 0 renders as the text "0".