
How to Create React Elements with JSX
JSX (JavaScript XML) is React’s syntax extension that lets you write HTML-like code directly in your JavaScript files, making component creation more intuitive and readable. Instead of dealing with verbose React.createElement() calls, JSX provides a declarative way to describe your UI structure, which gets transpiled to regular JavaScript under the hood. In this guide, you’ll learn how JSX works internally, master the syntax fundamentals, handle dynamic content and events, and discover performance optimization techniques that’ll make your React applications more maintainable and efficient.
How JSX Works Under the Hood
JSX isn’t magic – it’s syntactic sugar that gets transformed into React.createElement() calls during the build process. When you write JSX, tools like Babel convert it to function calls that create React elements, which are plain JavaScript objects describing what should appear on screen.
Here’s what happens during transformation:
// JSX code
const element = <h1 className="greeting">Hello, world!</h1>;
// Gets compiled to
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
The React.createElement() function returns an object like this:
{
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
},
key: null,
ref: null
}
This transformation process is handled by build tools during compilation, so there’s zero runtime overhead. The Babel JSX plugin handles this conversion seamlessly in most React setups.
Step-by-Step JSX Implementation Guide
Getting started with JSX requires understanding its syntax rules and best practices. Let’s walk through creating React elements progressively.
Basic Element Creation
Start with simple HTML-like elements:
// Simple elements
const header = <h1>Welcome to React</h1>;
const paragraph = <p>This is a paragraph</p>;
// Self-closing elements (required in JSX)
const image = <img src="logo.png" alt="Logo" />;
const lineBreak = <br />;
const input = <input type="text" placeholder="Enter name" />;
Adding Attributes and Props
JSX uses camelCase for HTML attributes and has some naming differences:
// Standard attributes (note the camelCase)
const styledDiv = (
<div
className="container"
id="main-content"
onClick={handleClick}
style={{backgroundColor: 'blue', fontSize: '16px'}}
>
Content here
</div>
);
// Data attributes and aria attributes use kebab-case
const accessibleButton = (
<button
data-testid="submit-btn"
aria-label="Submit form"
aria-disabled="false"
>
Submit
</button>
);
Embedding JavaScript Expressions
Use curly braces to embed JavaScript expressions in JSX:
const name = 'John';
const age = 25;
const items = ['apple', 'banana', 'orange'];
const userProfile = (
<div>
<h2>{name}'s Profile</h2>
<p>Age: {age}</p>
<p>Items: {items.length}</p>
<p>Status: {age >= 18 ? 'Adult' : 'Minor'}</p>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
Creating Custom Components
JSX really shines when creating reusable components:
// Function component
function UserCard({name, email, avatar}) {
return (
<div className="user-card">
<img src={avatar} alt={`${name}'s avatar`} />
<div className="user-info">
<h3>{name}</h3>
<p>{email}</p>
</div>
</div>
);
}
// Using the component
const userElement = (
<UserCard
name="Jane Doe"
email="jane@example.com"
avatar="/avatars/jane.jpg"
/>
);
Real-World Examples and Use Cases
Let’s explore practical JSX implementations you’ll encounter in production applications.
Dynamic Form Generation
function DynamicForm({fields, onSubmit}) {
const [formData, setFormData] = useState({});
const handleInputChange = (fieldName, value) => {
setFormData(prev => ({...prev, [fieldName]: value}));
};
return (
<form onSubmit={(e) => {e.preventDefault(); onSubmit(formData);}}>
{fields.map(field => (
<div key={field.name} className="form-group">
<label htmlFor={field.name}>{field.label}</label>
{field.type === 'select' ? (
<select
id={field.name}
value={formData[field.name] || ''}
onChange={(e) => handleInputChange(field.name, e.target.value)}
>
{field.options.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
) : (
<input
id={field.name}
type={field.type}
value={formData[field.name] || ''}
onChange={(e) => handleInputChange(field.name, e.target.value)}
required={field.required}
/>
)}
</div>
))}
<button type="submit">Submit</button>
</form>
);
}
Conditional Rendering Patterns
function Dashboard({user, notifications, isLoading}) {
if (isLoading) {
return <div className="spinner">Loading...</div>;
}
return (
<div className="dashboard">
{/* Conditional rendering with && */}
{user && (
<header className="welcome">
Welcome back, {user.name}!
</header>
)}
{/* Ternary operator for either/or scenarios */}
{user.isAdmin ? (
<AdminPanel />
) : (
<UserPanel />
)}
{/* Multiple conditions */}
{notifications.length > 0 && (
<div className="notifications">
<h3>Notifications ({notifications.length})</h3>
{notifications.map(notification => (
<div key={notification.id} className={`notification ${notification.type}`}>
{notification.message}
</div>
))}
</div>
)}
{/* Fallback content */}
{notifications.length === 0 && (
<p className="no-notifications">No new notifications</p>
)}
</div>
);
}
JSX vs. Alternative Approaches
Understanding when to use JSX versus other React element creation methods helps you make informed decisions.
Approach | Syntax Complexity | Readability | Performance | Build Setup Required | Best Use Case |
---|---|---|---|---|---|
JSX | Low | High | Identical (compiles to createElement) | Yes | Most React applications |
React.createElement() | High | Low | Baseline | No | Library development, no-build setups |
Template literals | Medium | Medium | Lower (no virtual DOM) | No | Simple HTML generation |
Hyperscript (h function) | Medium | Medium | Similar to createElement | Depends on implementation | Functional programming preference |
Performance Comparison
Here’s a benchmark comparing different approaches for creating 1000 list items:
Method | Bundle Size Impact | Runtime Performance | Developer Experience | Memory Usage |
---|---|---|---|---|
JSX (compiled) | 0kb (transforms away) | ~2.1ms | Excellent | Baseline |
createElement calls | 0kb additional | ~2.1ms | Poor for complex UI | Baseline |
Template strings | ~0.5kb | ~1.8ms | Good for simple cases | 10% less |
Best Practices and Common Pitfalls
Essential Best Practices
- Always close tags: Self-closing tags must end with /> in JSX
- Use fragments: Wrap multiple elements in React.Fragment or <></> instead of unnecessary divs
- Key props for lists: Always provide unique keys when rendering arrays
- Escape user content: JSX automatically escapes values to prevent XSS attacks
- Use camelCase: HTML attributes become camelCase props (onClick, className, etc.)
// Good practices
function TodoList({todos}) {
return (
<>
<h2>Todo List</h2>
{todos.map(todo => (
<div key={todo.id} className="todo-item">
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>
Delete
</button>
</div>
))}
</>
);
}
Common Pitfalls to Avoid
- Using array indices as keys: Can cause rendering issues when list order changes
- Forgetting to bind event handlers: Use arrow functions or proper binding
- Inline object creation in render: Creates new objects on every render, breaking memoization
- Missing dependency arrays: Can cause infinite re-renders with hooks
// Avoid these patterns
function BadComponent({items}) {
return (
<div>
{/* Bad: using index as key */}
{items.map((item, index) => (
<div key={index}>{item.name}</div>
))}
{/* Bad: inline object creation */}
<div style={{margin: '10px'}}>
Content
</div>
{/* Bad: creating new function on every render */}
<button onClick={() => console.log('clicked')}>
Click me
</button>
</div>
);
}
Performance Optimization Techniques
Optimize JSX rendering with these strategies:
// Memoize expensive computations
const ExpensiveComponent = React.memo(function ExpensiveComponent({data}) {
const processedData = useMemo(() => {
return data.map(item => ({
...item,
computed: expensiveCalculation(item)
}));
}, [data]);
return (
<div>
{processedData.map(item => (
<div key={item.id}>
<span>{item.name}</span>
<span>{item.computed}</span>
</div>
))}
</div>
);
});
// Extract static elements
const STATIC_HEADER = <h1>Static Header</h1>;
function OptimizedComponent({dynamicData}) {
return (
<div>
{STATIC_HEADER}
<DynamicContent data={dynamicData} />
</div>
);
}
Advanced JSX Patterns and Integration
JSX with TypeScript
TypeScript provides excellent JSX support with type checking:
interface ButtonProps {
variant: 'primary' | 'secondary' | 'danger';
size?: 'small' | 'medium' | 'large';
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
children: React.ReactNode;
disabled?: boolean;
}
function Button({variant, size = 'medium', onClick, children, disabled}: ButtonProps) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
onClick={onClick}
disabled={disabled}
type="button"
>
{children}
</button>
);
}
// Usage with full type safety
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log('Button clicked');
};
const typedButton = (
<Button variant="primary" size="large" onClick={handleClick}>
Click me
</Button>
);
Server-Side Rendering with JSX
JSX works seamlessly with server-side rendering frameworks. For applications deployed on VPS or dedicated servers, SSR can significantly improve initial page load times:
// Next.js example
import { GetServerSideProps } from 'next';
interface PageProps {
data: Array<{id: number; title: string}>;
}
export default function ServerRenderedPage({data}: PageProps) {
return (
<div>
<h1>Server-Rendered Content</h1>
{data.map(item => (
<article key={item.id}>
<h2>{item.title}</h2>
</article>
))}
</div>
);
}
export const getServerSideProps: GetServerSideProps = async () => {
const data = await fetchDataFromAPI();
return {props: {data}};
};
Understanding JSX fundamentals gives you the foundation to build complex, maintainable React applications. The declarative syntax makes your code more readable while the build-time transformation ensures optimal runtime performance. Whether you’re building simple components or complex applications, mastering these JSX patterns will make you more productive and help you write better React code.
For additional learning, check out the official React JSX documentation and the TypeScript JSX handbook for type-safe component development.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.