React JSX and Rendering Elements
JSX syntax, embedding expressions, rendering elements, fragments, and the Virtual DOM..
20+ years shipping production JavaScript and front-end systems at scale. Written from production experience, not tutorials.
- ✓Basic JavaScript knowledge (variables, functions, objects), Node.js v18 or higher, npm v9 or higher, a code editor (VS Code recommended), familiarity with the command line.
React JSX and Rendering Elements: A core React concept for building modern user interfaces. It helps you structure your components efficiently and handle data flow predictably.
React is a JavaScript library for building user interfaces. This article covers jsx rendering — a key concept for building modern web applications.
| Chrome | Firefox | Safari | Edge |
|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ |
A comprehensive guide to react jsx and rendering elements with production examples and best practices.
What Is JSX?
JSX is a syntax extension for JavaScript that looks like HTML but compiles to React.createElement calls. It's not a template language—it's syntactic sugar that lets you describe UI structure declaratively. Under the hood, Babel transforms JSX into plain JavaScript objects called React elements. This means you can embed any JavaScript expression inside JSX using curly braces. JSX prevents injection attacks by default because React DOM escapes any values embedded before rendering. In production, always use the production build of React which skips prop type validation and other development warnings for better performance.
Embedding Expressions in JSX
You can embed any JavaScript expression inside JSX by wrapping it in curly braces. This includes variables, function calls, and even JSX itself. However, statements (like if/else) are not allowed—use ternary operators or logical && instead. For arrays, JSX automatically joins them with no separator; use .join(', ') if you need commas. Be careful with falsy values like 0 or empty strings: they render as text. A common bug is rendering a count of 0 when the array is empty because 0 is falsy but React renders it. Always use conditional rendering explicitly.
Boolean() to coerce.JSX Attributes and Styling
JSX attributes follow camelCase naming: className, htmlFor, onClick, etc. Inline styles are written as JavaScript objects with camelCase properties. For dynamic class names, use template literals or libraries like classnames. Avoid inline styles for production—they bypass CSS optimizations and increase specificity issues. Instead, use CSS modules or styled-components. Remember that style objects must have numeric values without units for most properties (React appends 'px' automatically for some properties, but not all—test it).
Rendering Elements with ReactDOM
React elements are the smallest building blocks of React apps. To render an element into the DOM, use ReactDOM.createRoot() and call render(). The root DOM node is typically a single <div> in your HTML. React elements are immutable—once created, you cannot change its children or attributes. To update the UI, create a new element and pass it to render() again. React efficiently updates only the changed parts using its virtual DOM diffing algorithm. In production, always use the production build of React and ReactDOM to disable development warnings and enable performance optimizations.
Updating the Rendered Element
React elements are immutable, so to update the UI you create a new element and pass it to root.render(). React compares the new element with the previous one and applies minimal DOM updates. This is efficient for simple cases, but for complex UIs you'll use state management. A common mistake is calling render() multiple times on the same root—this is fine for learning but in production you should use components with state. The render() function is synchronous and blocks the browser until the DOM is updated.
render() manually is rare in production—use state and setState inside components to trigger re-renders.render() with a new element; React diffs efficiently.React Only Updates What's Necessary
React's virtual DOM diffing ensures that only the parts of the real DOM that changed are updated. Even if you re-render the entire tree, React compares the new virtual DOM with the previous snapshot and applies minimal patches. This makes React fast for most use cases. However, unnecessary re-renders can still cause performance issues. Use React.memo, useMemo, and useCallback to prevent wasteful re-renders in production. Also, avoid creating new objects or functions in render unless necessary, as they break referential equality.
JSX Prevents Injection Attacks
By default, React DOM escapes any values embedded in JSX before rendering them. This means you cannot inject arbitrary HTML or scripts via user input. However, if you need to render raw HTML (e.g., from a CMS), use dangerouslySetInnerHTML—but only after sanitizing the input. Never use it with user-generated content. Cross-site scripting (XSS) vulnerabilities often arise from bypassing React's escaping. In production, always sanitize HTML with a library like DOMPurify before using dangerouslySetInnerHTML.
JSX and Components
JSX is not limited to HTML tags—you can also render custom React components. Component names must start with a capital letter to distinguish them from HTML tags. Props are passed as JSX attributes and accessed as an object inside the component. Components can be functions or classes (though functions are preferred). In production, use functional components with hooks for better performance and simpler code. Avoid creating components inside render or other components—they cause unnecessary re-mounting.
Composing Components with JSX
Components can be composed together to build complex UIs. JSX makes this natural: you can nest components just like HTML. Props flow downward from parent to child. This unidirectional data flow makes debugging easier. In production, keep components small and focused. Extract reusable UI pieces into separate components. Avoid deeply nested prop drilling—use context or state management libraries for global data. Also, remember that JSX expressions can include components, so you can conditionally render them.
JSX and the Virtual DOM
JSX is compiled to React.createElement calls, which produce plain JavaScript objects called React elements. These elements form a virtual DOM tree. When state changes, React creates a new virtual DOM tree and diffs it against the previous one. The diffing algorithm (reconciliation) determines the minimal set of DOM mutations. Understanding this helps you write efficient code. For example, using a stable key prop on list items helps React reuse DOM nodes. In production, avoid mutating state directly—always use setState or useState updater functions to trigger proper reconciliation.
Production Best Practices for JSX
When deploying a React app, always use the production build. This minifies code, strips out development warnings, and enables performance optimizations like the production version of React. Use tools like Create React App, Vite, or Next.js which handle this automatically. For custom setups, set NODE_ENV=production and use TerserPlugin. Also, consider using React.lazy and Suspense for code splitting. Avoid large JSX trees in a single component—split them. Use ESLint with the react-plugin to catch common mistakes like missing keys or dangerous patterns.
Common JSX Pitfalls and How to Avoid Them
Newcomers often trip over several JSX quirks. First, JSX requires a single root element—use fragments (<></>) to avoid extra divs. Second, comments must be inside curly braces: {/ comment /}. Third, self-closing tags like <img /> must have a slash. Fourth, conditional rendering using && can render 0 or NaN—use ternary or Boolean(). Fifth, spread attributes ({...props}) can override explicit props—order matters. In production, these bugs cause subtle UI issues. Always test edge cases like empty arrays or null values.
How JSX Compiles: Classic vs Automatic Runtime
JSX is not valid JavaScript; it must be compiled into JavaScript function calls. React traditionally uses React.createElement to transform JSX into elements. However, starting with React 17, a new JSX transform was introduced that uses and jsx() from jsxs()react/jsx-runtime, eliminating the need to import React in every file.
Classic Runtime (React.createElement) In the classic transform, JSX like <h1>Hello</h1> compiles to React.createElement('h1', null, 'Hello'). This requires import React from 'react' in every file that uses JSX.
Automatic Runtime (jsx() transform) With the automatic runtime, the same JSX compiles to import { jsx as _jsx } from 'react/jsx-runtime'; _jsx('h1', { children: 'Hello' }). This transform is enabled by setting "runtime": "automatic" in Babel or TypeScript config. It automatically imports the necessary functions, so you no longer need to import React just for JSX.
Key Differences - The automatic runtime produces slightly smaller bundles because it doesn't include the entire React object. - The automatic runtime supports fragment shorthand (<></>) without explicit import. - The classic runtime is still supported for backward compatibility.
To switch to automatic runtime, update your Babel config: ``json { "presets": [ ["@babel/preset-react", { "runtime": "automatic" }] ] } ` Or for TypeScript, set "jsx": "react-jsx" in tsconfig.json`.
Understanding the compilation helps debug issues like missing imports or unexpected behavior when upgrading React versions.
JSX Converter Tools
When migrating existing HTML markup to React, manually converting HTML to JSX can be tedious. JSX converter tools automate this process by transforming HTML strings into valid JSX syntax, handling attribute changes (e.g., class to className, for to htmlFor), self-closing tags, and inline styles.
Popular Tools - HTML to JSX (https://htmltojsx.com/): A web-based converter that instantly converts HTML snippets to JSX. It handles most common transformations and allows copying the result. - Babel Plugin: The @babel/plugin-transform-react-jsx can be used with a custom pragma to convert HTML-like syntax, but it's more complex. - VS Code Extensions: Extensions like "HTML to JSX" provide in-editor conversion, useful for large codebases.
Example Conversion Input HTML: ``html <div class="container" id="main"> <p style="color: red;">Hello World</p> </div> ` Output JSX: `jsx <div className="container" id="main"> <p style={{ color: 'red' }}>Hello World</p> </div> ``
Limitations - Inline event handlers (e.g., onclick) are not automatically converted to React events. - Complex HTML structures may require manual adjustment for React components. - Tools may not handle all edge cases, so always review the output.
Using converter tools accelerates migration of static HTML to React components, especially when dealing with large templates from design systems or legacy code.
Keyed Fragments
When rendering lists in React, each element should have a unique key prop for efficient reconciliation. However, when using fragments (<></> or <Fragment>), you cannot directly add a key to the shorthand syntax. Instead, use the explicit <Fragment key={id}> pattern to assign keys to fragments.
Why Keyed Fragments? Sometimes you need to render multiple sibling elements without a wrapper div, but still need a key for each item in a list. For example, rendering a list of items where each item consists of multiple elements (e.g., a row with multiple columns). Using a keyed fragment allows React to track each group efficiently.
Example ```jsx import React, { Fragment } from 'react';
function ItemList({ items }) { return ( <div> {items.map(item => ( <Fragment key={item.id}> <h2>{item.title}</h2> <p>{item.description}</p> </Fragment> ))} </div> ); } ```
Important Notes - The shorthand <></> does not support keys; you must use <Fragment>. - Keys should be unique among siblings, just like with regular elements. - Keyed fragments are particularly useful when you cannot add a wrapper div due to CSS or layout constraints.
Performance Impact Using keyed fragments helps React's diffing algorithm identify which items changed, added, or removed, leading to more efficient updates. Without keys, React may re-render all items unnecessarily.
Always use stable, unique identifiers (like database IDs) for keys, not array indices, to avoid rendering bugs.
<Fragment key={id}> to assign keys to fragment groups in lists, enabling efficient reconciliation without adding extra DOM nodes.| File | Command / Code | Purpose |
|---|---|---|
| App.jsx | const element = Hello, world!; | What Is JSX? |
| ExpressionExample.jsx | const user = { name: 'Alice', age: 30 }; | Embedding Expressions in JSX |
| StylingExample.jsx | const divStyle = { | JSX Attributes and Styling |
| index.jsx | const element = Hello, world!; | Rendering Elements with ReactDOM |
| Timer.jsx | function tick() { | Updating the Rendered Element |
| DiffExample.jsx | const element = ( | React Only Updates What's Necessary |
| SafeHTML.jsx | const userInput = ''; | JSX Prevents Injection Attacks |
| ComponentExample.jsx | function Welcome(props) { | JSX and Components |
| Composition.jsx | function Avatar(props) { | Composing Components with JSX |
| VirtualDOM.jsx | const element = Hello ; | JSX and the Virtual DOM |
| build.sh | npm run build | Production Best Practices for JSX |
| Pitfalls.jsx | return <>TitleBody >; | Common JSX Pitfalls and How to Avoid Them |
| jsx-compile-example.js | const element = Hello; | How JSX Compiles |
| html-to-jsx-example.js | const html = ` | JSX Converter Tools |
| keyed-fragments-example.jsx | function BlogPosts({ posts }) { | Keyed Fragments |
Key takeaways
Common mistakes to avoid
3 patternsNot understanding React component re-rendering
Ignoring the rules of hooks
Mutating state directly instead of using setState
Interview Questions on This Topic
What is the Virtual DOM and how does React use it?
Frequently Asked Questions
20+ years shipping production JavaScript and front-end systems at scale. Written from production experience, not tutorials.
That's React. Mark it forged?
6 min read · try the examples if you haven't