JSX stands for JavaScript XML. It's a syntax extension for JavaScript that allows you to write HTML-like code within your JavaScript files. JSX makes it more intuitive to create and visualize the structure of your UI components in React.
To truly understand the value of JSX, let's compare how we would create UI elements with and without it.
Without JSX (Pure JavaScript)
// Without JSX, we'd use createElementS directlyimport { createElement } from"react";constelement=createElement("h1", { className:"greeting" },"Hello, World!");// Nested elements become verbose and hard to readconstcontainer=createElement("div", { className:"container" },createElement("h1",null,"Title"),createElement("p", { className:"content" },"Some paragraph text",createElement("a", { href:"/link" },"Click here") ));
With JSX (in TSX file)
// The same elements, but with JSXconstElement= () => {return <h1className="greeting">Hello, World!</h1>;};// Nested elements are much more readableconstContainer= () => {return ( <divclassName="container"> <h1>Title</h1> <pclassName="content"> Some paragraph text <ahref="/link">Click here</a> </p> </div> );};
As you can see, JSX makes the code much more readable and intuitive, especially for nested elements. Behind the scenes, JSX is transformed into createElement() calls during the build process.
TypeScript enhances JSX by adding static type checking. This helps catch errors at compile time rather than at runtime.
Type Definitions for JSX Elements
React with TypeScript provides several types for JSX elements:
// Basic JSX elementconstheading:JSX.Element= <h1>Welcome</h1>;// For a function componentconstGreeting= ():JSX.Element=> {return <p>Hello there!</p>;};
Keys help React identify which items have changed, been added, or removed. Without keys, React would have to re-render the entire list whenever any item changes.
// Poor performance example (without unique keys)constBadListExample= () => {constitems:string[] = ["Apple","Banana","Cherry"];return ( <ul> {items.map((item, index) => (// Using index as key is generally NOT recommended when the list order might change <likey={index}>{item}</li> ))} </ul> );};// Good performance example (with unique keys)interfaceListItem { id:string; name:string;}constGoodListExample= () => {constitems:ListItem[] = [ { id:"fruit-1", name:"Apple" }, { id:"fruit-2", name:"Banana" }, { id:"fruit-3", name:"Cherry" }, ];return ( <ul> {items.map((item) => ( <likey={item.id}>{item.name}</li> ))} </ul> );};
Fragments are a special feature in React that allows you to group multiple elements together without adding an extra node to the DOM. In standard HTML, you can't return multiple adjacent elements without a parent container. React fragments solve this problem.
Fragments address a fundamental limitation in JSX: a component must return a single root element. Before fragments, developers would often add unnecessary <div> elements as wrappers, which could cause styling issues or break semantic HTML structure.
How to Use Fragments
React provides two syntaxes for fragments:
Long syntax (when you need to provide attributes like key):
The <>...</> syntax is shorthand for <Fragment>...</Fragment>. Use the long syntax when you need to provide a key attribute.
Why Fragments Are Important
Fragments provide several benefits:
Cleaner DOM: They don't add unnecessary nodes to the DOM tree
Faster rendering: Fewer DOM nodes means slightly better performance
Less CSS side effects: No extra wrapper elements to interfere with CSS selectors, flexbox, or grid layouts
Semantic HTML: Allows you to maintain proper HTML structure (like table rows within tables)
Compare these two approaches:
// Without fragments (adds extra div to DOM)constWithoutFragment= () => {return ( <div> {/* Extra div that might break styling or semantics */} <h1>Title</h1> <p>Paragraph</p> </div> );};// With fragments (no extra DOM node)constWithFragment= () => {return ( <> <h1>Title</h1> <p>Paragraph</p> </> );};
Fragments help maintain clean DOM structure and avoid styling issues that might be caused by extra container elements, making them an essential feature in modern React development.
Comments in JSX must be wrapped in curly braces and use JavaScript's multi-line comment syntax:
constElement= () => {return ( <div> {/* This is a comment inside JSX */} <p>This is visible text</p> {/* Multi-line comments work too */} {false&&"This won't be rendered"} </div> )};
TypeScript allows us to strictly type the children elements:
import { ReactNode } from"react";// For a component that only accepts string childreninterfaceTextBoxProps { children:string;}constTextBox= ({ children }:TextBoxProps) => {return <divclassName="text-box">{children}</div>;};// For a component that accepts specific component typesinterfaceDialogProps { title:string; children:ReactNode;}constDialog= ({ title, children }:DialogProps) => {return ( <divclassName="dialog"> <divclassName="dialog-header"> <h2>{title}</h2> </div> <divclassName="dialog-content">{children}</div> </div> );};
Never directly inject user input as HTML, as it can lead to XSS attacks:
// DON'T do thisconstComment= (props: { content:string }) => {return <divdangerouslySetInnerHTML={{ __html:props.content }} />;};// DO this insteadconstComment= (props: { content:string }):JSX.Element=> {return <div>{props.content}</div>;};
Case Sensitivity
JSX is case-sensitive. All built-in HTML elements start with lowercase, while custom React components should start with uppercase:
// HTML element - lowercaseconstparagraph= <p>This is a paragraph</p>;// Custom component - uppercaseconstMyComponent= () => {return <div>My Component</div>;};constelement= <MyComponent />;
String Attributes vs Expression Attributes
Be careful with the syntax for string literals versus expressions:
// String literals use quotes<divid="main-content"className="container"></div>// JavaScript expressions use curly bracesconstdynamicId="dynamic-content";constactiveClass="active";<divid={dynamicId} className={`container ${activeClass}`}></div>// Common mistake: using quotes around expressions<divid="{dynamicId}"></div> // WRONG - This will render the literal string "{dynamicId}"
Boolean Attributes
In JSX, boolean attributes can be specified without a value:
// These are equivalent<buttondisabled={true}>Disabled Button</button><buttondisabled>Disabled Button</button>// False values must be explicitly specified<buttondisabled={false}>Enabled Button</button>
JSX is a powerful syntax extension that bridges HTML and JavaScript, making UI development more intuitive and maintainable. With TypeScript integration through .tsx files, you gain additional benefits of static type checking, which helps catch errors early and provides better developer tooling.
Key takeaways:
JSX makes UI code more readable and intuitive than raw React.createElement() calls
Use .tsx file extension for any file containing JSX syntax
Use .ts file extension for non-UI related code and utilities
TypeScript enhances JSX with static typing for props, events, and elements
JSX looks like HTML but has important differences (camelCase, className, self-closing tags)
Fragments help create cleaner component structures
As you continue your React journey, you'll find that JSX with TypeScript becomes a natural and efficient way to express your UI components with increased safety and developer experience.