What is JSX?
JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code directly in JavaScript. It makes React code more readable and expressive. JSX gets compiled to regular JavaScript function calls using React.createElement.
JSX Basics
React JSXRead-only
1
// JSX syntax const element = <h1 className="greeting">Hello, world!</h1>; // Compiled to const element = React.createElement('h1', { className: 'greeting' }, 'Hello, world!'); // Multiple elements must be wrapped const app = ( <div> <h1>Title</h1> <p>Paragraph</p> </div> ); // Self-closing tags const img = <img src="image.jpg" alt="Description" />; const input = <input type="text" placeholder="Enter name" />; // Empty tags const fragment = <></>; const br = <br />;
JSX Expressions
React JSXRead-only
1
function Greeting({ user, items }) { const name = 'John'; const age = 25; return ( <div> {/* Embedding JavaScript expressions with curly braces */} <h1>Hello, {name}!</h1> <p>Age: {age}</p> {/* Expressions can be any valid JavaScript */} <p>Next year you'll be {age + 1}</p> <p>Uppercase: {name.toUpperCase()}</p> <p>Condition: {age >= 18 ? 'Adult' : 'Minor'}</p> {/* Function calls */} <p>Formatted: {formatDate(new Date())}</p> {/* Object properties */} <p>User: {user?.name ?? 'Guest'}</p> {/* Arrays are joined */} <p>Items: {['apple', 'banana', 'orange']}</p> {/* Numbers and strings work */} <p>{42}</p> <p>{'string literal'}</p> {/* null, undefined, false are ignored */} <p>{null}</p> <p>{undefined}</p> <p>{false}</p> </div> ); }
JSX Attributes
React JSXRead-only
1
// HTML attributes use camelCase const button = <button className="btn" onClick={handleClick}>Click</button>; const input = <input type="text" maxLength={10} readOnly />; const label = <label htmlFor="name">Name:</label>; // Special attributes const svg = <svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" /></svg>; // Boolean attributes (can omit value) const checkbox = <input type="checkbox" checked={isChecked} />; const disabled = <button disabled={isDisabled}>Submit</button>; // Spread attributes const props = { className: "btn", type: "button", onClick: handleClick }; const spreadButton = <button {...props}>Click</button>; // Custom attributes (data-* attributes) const custom = <div data-id="123" data-custom-attribute="value">Content</div>; // Inline styles (camelCase, object) const styleButton = ( <button style={{ backgroundColor: 'blue', color: 'white', padding: '10px 20px', borderRadius: '4px', fontSize: '16px' }}> Styled Button </button> ); // Dynamic styles const dynamicStyle = { color: isActive ? 'green' : 'red', fontWeight: isBold ? 'bold' : 'normal' }; <p style={dynamicStyle}>Dynamic text</p>
Conditional Rendering in JSX
React JSXRead-only
1
function ConditionalRender({ isLoggedIn, user, items, error }) { // 1. If-else outside JSX if (error) { return <ErrorMessage error={error} />; } if (!items.length) { return <EmptyState />; } // 2. Ternary operator return ( <div> <h1>Welcome</h1> {isLoggedIn ? ( <UserDashboard user={user} /> ) : ( <LoginButton /> )} {/* 3. Logical && operator */} {isLoggedIn && <WelcomeMessage user={user} />} {items.length > 0 && <ItemList items={items} />} {/* 4. IIFE for complex logic */} {(() => { if (isLoggedIn && user.isAdmin) { return <AdminPanel />; } else if (isLoggedIn) { return <UserPanel />; } return <GuestPanel />; })()} {/* 5. Variable assignment */} {(() => { let content; if (isLoggedIn) { content = <Profile user={user} />; } else { content = <LoginForm />; } return content; })()} </div> ); }
Rendering Lists
React JSXRead-only
1
function TodoList({ todos, categories }) { // Basic list rendering return ( <ul> {todos.map(todo => ( <li key={todo.id}> {todo.text} </li> ))} </ul> ); // List with conditional rendering return ( <div> {todos.map(todo => ( <div key={todo.id} className={todo.completed ? 'completed' : 'active'}> <input type="checkbox" checked={todo.completed} readOnly /> <span>{todo.text}</span> {todo.priority === 'high' && <span className="badge">High Priority</span>} </div> ))} </div> ); // Filtered list const activeTodos = todos.filter(todo => !todo.completed); return ( <ul> {activeTodos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> ); // Map with index (use only for static lists) return ( <ul> {categories.map((category, index) => ( <li key={index}>{category}</li> ))} </ul> ); // Nested lists return ( <div> {categories.map(category => ( <div key={category.id}> <h3>{category.name}</h3> <ul> {category.items.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> </div> ))} </div> ); }
Fragments
React JSXRead-only
1
// Problem: Adjacent JSX elements must be wrapped function WithoutFragment() { return ( <div> {/* Unnecessary div */} <h1>Title</h1> <p>Content</p> </div> ); } // Solution 1: React Fragment (explicit) import { Fragment } from 'react'; function WithFragment() { return ( <Fragment> <h1>Title</h1> <p>Content</p> </Fragment> ); } // Solution 2: Short syntax (<> </>) function WithShortFragment() { return ( <> <h1>Title</h1> <p>Content</p> </> ); } // Fragment with key (for mapping) function FragmentList({ items }) { return ( <dl> {items.map(item => ( <Fragment key={item.id}> <dt>{item.term}</dt> <dd>{item.description}</dd> </Fragment> ))} </dl> ); } // Fragment with children pattern function Table({ data }) { return ( <table> <tbody> {data.map(row => ( <Fragment key={row.id}> {row.cells.map(cell => ( <tr key={cell.id}> <td>{cell.value}</td> </tr> ))} </Fragment> ))} </tbody> </table> ); }
JSX Children
React JSXRead-only
1
function Card({ children, header, footer }) { return ( <div className="card"> {header && <div className="card-header">{header}</div>} <div className="card-body">{children}</div> {footer && <div className="card-footer">{footer}</div>} </div> ); } // String literal children <div>Hello World</div> // JSX children <div> <h1>Title</h1> <p>Content</p> </div> // Expression children <div>{user.name}</div> // Function as children (render props) <DataProvider> {(data) => <Display data={data} />} </DataProvider> // Multiple children types <Container> <Header /> {content} <Footer /> {isLoading && <Spinner />} </Container> // Children manipulation export function List({ children, separator }) { const array = React.Children.toArray(children); return ( <div> {array.map((child, index) => ( <React.Fragment key={index}> {child} {index < array.length - 1 && separator} </React.Fragment> ))} </div> ); } // Usage <List separator={<hr />}> <div>Item 1</div> <div>Item 2</div> <div>Item 3</div> </List>
JSX Gotchas & Common Mistakes
React JSXRead-only
1
// ❌ Wrong: class instead of className <div class="container">Content</div> // ✅ Correct <div className="container">Content</div> // ❌ Wrong: for instead of htmlFor <label for="name">Name:</label> // ✅ Correct <label htmlFor="name">Name:</label> // ❌ Wrong: inline style as string <div style="color: red">Text</div> // ✅ Correct <div style={{ color: 'red' }}>Text</div> // ❌ Wrong: comments in JSX <div> // This is a comment <p>Text</p> </div> // ✅ Correct <div> {/* This is a comment */} <p>Text</p> </div> // ❌ Wrong: if statements inside JSX <div> {if (condition) { <p>True</p> }} </div> // ✅ Correct: ternary or && <div> {condition ? <p>True</p> : <p>False</p>} </div> // ❌ Wrong: multiple root elements return ( <h1>Title</h1> <p>Content</p> ); // ✅ Correct: wrap with fragment return ( <> <h1>Title</h1> <p>Content</p> </> ); // ❌ Wrong: reserved words <div {...props}></div> // 'for', 'class' not allowed // ❌ Wrong: unescaped entities <div>© 2024</div> // Works but not recommended // ✅ Correct: use dangerouslySetInnerHTML or literal <div>© 2024</div> <div dangerouslySetInnerHTML={{ __html: '© 2024' }} />
JSX Best Practices
- Use parentheses for multi-line JSX – Improves readability
- Keep expressions simple – Extract complex logic outside JSX
- Use fragments to avoid extra DOM nodes – Reduces nesting
- Always add keys when mapping – Helps React identify items
- Use camelCase for all HTML attributes – Consistent with React
- Self-close tags with no children – Cleaner syntax
- Use template literals for dynamic strings – Instead of concatenation
- Extract repeated JSX into components – DRY principle
- Use boolean attributes without values – Cleaner when true
- Avoid inline functions in render – Extract to handlers
JSX Transformations (React 17+)
React JSXRead-only
1
// Modern JSX transform (React 17+) // No need to import React function App() { return <h1>Hello World</h1>; } // Compiles to import { jsx as _jsx } from 'react/jsx-runtime'; function App() { return _jsx('h1', { children: 'Hello World' }); } // Classic transform (before React 17) import React from 'react'; function App() { return <h1>Hello World</h1>; } // Compiles to import React from 'react'; function App() { return React.createElement('h1', null, 'Hello World'); } // Configuration in tsconfig.json { "compilerOptions": { "jsx": "react-jsx" // Modern transform // "jsx": "react" // Classic transform } }
Conclusion
JSX is a powerful syntax extension that makes React code more readable and maintainable. It combines the power of JavaScript with the familiarity of HTML. Understanding JSX fundamentals, expressions, conditional rendering, and common patterns is essential for effective React development.