---
title: "TSX/JSX Syntax Cheatsheet"
slug: "tsx-jsx-syntax"
category: "cheatsheets"
tags: ["cheatsheets", "tsx", "jsx", "react", "typescript", "frontend", "components"]
status: "stable"
last_updated: 2026-05-14
summary: "TSX/JSX syntax patterns: interpolations, ternaries, fragments, key prop, conditional rendering, event handlers, and ref usage."
related:
  [
    "[[frontend/react]]",
    "[[frontend/nextjs]]",
    "[[cheatsheets/react-hooks]]",
    "[[cheatsheets/typescript-narrowing-patterns]]",
    "[[cheatsheets/typescript-utility-types]]",
    "[[frontend/tailwind]]",
    "[[cheatsheets/index|Cheatsheets]]",
  ]
---

## Overview

JSX is syntactic sugar for `React.createElement` calls; TSX is JSX with TypeScript's type system on top. Understanding the desugared form explains most of the edge cases around keys, fragments, and conditional rendering. This card is a quick reference for the patterns you use in every component. For React hooks see [[cheatsheets/react-hooks]]; for TypeScript type patterns see [[cheatsheets/typescript-narrowing-patterns]].

## Interpolation and expressions

Curly braces `{}` switch from JSX to JavaScript. Any JS expression is valid inside them; statements are not.

| Pattern | Example | Notes |
|---|---|---|
| Variable | `<p>{name}</p>` | Renders `String(name)` |
| Arithmetic | `<p>{count * 2}</p>` | Evaluated at render |
| Method call | `<p>{items.join(", ")}</p>` | Return value rendered |
| Template literal | `` <p>{`Hello, ${name}`}</p> `` | Nested template inside `{}` |
| Spread props | `<Input {...props} />` | Spread object as props |
| Prop expression | `<Button disabled={!isReady} />` | Boolean from expression |
| String prop | `<p className="foo" />` | Strings need no `{}` |
| Event handler | `<button onClick={() => doThing()}>` | Arrow function or reference |
| Ref callback | `<div ref={el => (ref.current = el)}>` | Imperative DOM access |

`0` renders as the string `"0"` in JSX. Guard with a boolean: `{count > 0 && <Badge />}` not `{count && <Badge />}`.

## Conditional rendering

Four patterns; each has the right niche.

| Pattern | Syntax | Use when |
|---|---|---|
| Ternary | `{cond ? <A /> : <B />}` | Two branches, neither is null |
| Short-circuit | `{cond && <A />}` | Show or show nothing; guard against `0` |
| Nullish coalesce | `{value ?? <Fallback />}` | Show fallback only when `value` is null/undefined |
| Variable | `const el = cond ? <A /> : null; return <>{el}</>` | Complex logic before the return |
| Early return | `if (!data) return <Spinner />;` | Guard clauses; avoids deep nesting |

Prefer early return over nested ternaries. A component that returns different roots based on loading/error/data states is easier to read with three early-return guards than one deeply nested ternary tree.

## Fragments

Fragments let you return multiple sibling elements without adding a DOM node.

| Pattern | Syntax | Notes |
|---|---|---|
| Short fragment | `<>...</>` | No props allowed |
| Long fragment | `<React.Fragment>...</React.Fragment>` | Accepts `key` prop |
| Fragment with key | `<React.Fragment key={id}>...</React.Fragment>` | Required when mapping |
| Avoid extra div | `return <><A /><B /></>;` | Common wrapper replacement |

Use the long form only when you need the `key` prop. Otherwise prefer `<>...</>`.

## Lists and the key prop

React uses `key` to reconcile list items across renders. Keys must be stable and unique among siblings.

| Pattern | Example | Notes |
|---|---|---|
| Map with key | `items.map(i => <Li key={i.id} />)` | Use stable id, not index |
| Index as key | `items.map((i, idx) => <Li key={idx} />)` | Only when list never reorders |
| Fragment key | `items.map(i => <React.Fragment key={i.id}>…</React.Fragment>)` | Multiple nodes per item |
| Derived key | `key={${cat}-${i.id}}` | Composite when ids collide across categories |

Never use `Math.random()` or `Date.now()` as keys. They change on every render and destroy component state.

## TypeScript-specific TSX patterns

TSX adds type annotations to props, events, and refs.

| Pattern | Syntax | Notes |
|---|---|---|
| Typed props | `function Card({ title }: { title: string })` | Inline type |
| Interface props | `interface Props { title: string }` then `function Card({ title }: Props)` | Preferred for reuse |
| Children | `children: React.ReactNode` | Most permissive type for children |
| Event handler | `onChange: React.ChangeEvent<HTMLInputElement>` | Fully typed event |
| Click handler | `onClick: React.MouseEvent<HTMLButtonElement>` | Button-specific mouse event |
| Ref | `ref: React.RefObject<HTMLDivElement>` | From `useRef<HTMLDivElement>(null)` |
| Generic component | `function List<T>({ items }: { items: T[] })` | Add trailing comma in TSX: `<T,>` |
| `as const` assertion | `const v = ["a", "b"] as const` | Narrows to tuple literal |

In `.tsx` files, `<T>` alone is ambiguous with JSX. Write `<T,>` or `<T extends unknown>` for generic arrow functions.

## Common gotchas

- `{0 && <Component />}` renders `0` to the DOM. Use `{count > 0 && <Component />}` or a ternary.
- JSX attribute names are camelCase (`className`, `htmlFor`, `tabIndex`). HTML attributes in lowercase will work but produce a warning.
- A component function must return `JSX.Element`, `ReactNode`, or `null`. Returning `undefined` throws in React 17 and below.
- The `key` prop is not accessible inside the component via `props.key`. Pass the id separately if the component needs it.
- Self-closing syntax (`<Comp />`) is required for JSX; HTML allows `<Comp>` without a close tag but JSX does not.
- Fragments lose their key if you switch from `<React.Fragment key={k}>` to `<>`. The key is silently dropped.

## Related

- [[frontend/react]]
- [[frontend/nextjs]]
- [[cheatsheets/react-hooks]]
- [[cheatsheets/typescript-narrowing-patterns]]
- [[cheatsheets/typescript-utility-types]]
- [[frontend/tailwind]]
- [[cheatsheets/index|Cheatsheets]]
