BeginnerReact Concepts

What is JSX in React? [With 15 Practical Examples]

JSX is the syntax that allows HTML and JavaScript to live together in React. This article demystifies JSX, showing how it improves component readability and how to embed expressions, apply styles, handle lists, and use fragments for cleaner UI code.

By Rudraksh Laddha

My first React project File Manage (Which Not Live at that time), I was debugging a component that kept throwing "Adjacent JSX elements must be wrapped in an enclosing tag" errors. My HTML background made me think I could just slap divs everywhere, but I was missing the fundamental difference:

const element = <h1>Hello World</h1>;

This isn't HTML. It's not even a string. It's JSX - and understanding this distinction changed how I write React components.

Here's what I wish someone had told me from day one.


What JSX Actually Is (And Why It Exists)

JSX stands for JavaScript XML, but that name is misleading. It's really a syntax extension that lets you describe UI structure using familiar HTML-like syntax within JavaScript.

When I started with React, I thought JSX was just "HTML in JavaScript." But after building dozens of components, I realized JSX solves a specific problem: how do you describe complex UI structures without drowning in function calls?

Without JSX (createElement):

React.createElement(
  'div',
  { className: 'container' },
  React.createElement('h1', null, 'Title'),
  React.createElement('p', null, 'Content')
);

With JSX:

<div className="container">
  <h1>Title</h1>
  <p>Content</p>
</div>

The second version is clearer, right? That's JSX doing its job - making component structure scannable and maintainable.


Why JSX Won (Despite Initial Resistance)

Early React developers actually hated JSX. Mixing HTML into JavaScript felt like breaking separation of concerns. But after working with large React codebases, I understand why it became the standard:

JSX acknowledges reality:

In component-based architecture, your rendering logic is your UI logic. They're not separate concerns - they're the same concern expressed in code.

Instead of artificially separating HTML templates from JavaScript behavior (like Angular or Vue), JSX puts everything in one place where it belongs.


15 JSX Patterns I Use in Production

These aren't just syntax rules - they're patterns I reach for when building real applications. Each one solves specific problems I've encountered.

1. Expression Interpolation (The Foundation)

Any JavaScript expression works inside {}. This is where JSX's power comes from:

const user = { name: "Sarah", role: "admin" };
const timestamp = new Date().toLocaleDateString();

const element = (
  <div>
    <h1>Welcome back, {user.name}</h1>
    <span className={user.role === 'admin' ? 'badge-admin' : 'badge-user'}>
      {user.role.toUpperCase()}
    </span>
    <time>Last login: {timestamp}</time>
  </div>
);

⚠️ Common mistake: Using {} for static strings. <h1>{"Hello"}</h1> is unnecessary - just use <h1>Hello</h1>.

2. Single Parent Requirement (And How to Handle It)

Every JSX expression must return a single parent. I use fragments to avoid wrapper divs that mess up CSS:

// ❌ This breaks
return (
  <h1>Title</h1>
  <p>Content</p>
);

// ✅ Fragment solution
return (
  <>
    <h1>Title</h1>
    <p>Content</p>
  </>
);

// ✅ Or explicit React.Fragment for keys
return (
  <React.Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.definition}</dd>
  </React.Fragment>
);

3. camelCase Attributes (HTML vs JSX Differences)

// HTML attributes become camelCase JSX props
const formElement = (
  <div>
    <label htmlFor="email">Email</label>
    <input 
      id="email"
      className="form-input"
      tabIndex={1}
      autoComplete="email"
      onChange={handleChange}
    />
  </div>
);

💡 Pro tip: Most linters will catch HTML attribute mistakes. Use them!

4. Conditional Rendering (Patterns That Scale)

Learn more about React Conditional Rendering

I prefer different patterns based on complexity:

// Simple boolean: use &&
const Notification = ({ message, type }) => (
  <div>
    {message && <Alert type={type}>{message}</Alert>}
  </div>
);

// Binary choice: ternary operator
const UserStatus = ({ isOnline }) => (
  <span className={isOnline ? 'status-online' : 'status-offline'}>
    {isOnline ? '🟢 Online' : '🔴 Offline'}
  </span>
);

// Complex conditions: extract to variable
const Dashboard = ({ user, permissions }) => {
  const canViewAnalytics = user.role === 'admin' || permissions.includes('analytics');
  
  return (
    <div>
      <h1>Dashboard</h1>
      {canViewAnalytics && <AnalyticsPanel />}
    </div>
  );
};

⚠️ Watch out for falsy values: {0 && <Component />} renders "0", not nothing.

5. JSX as Variables (Composition Pattern)

Storing JSX in variables makes complex components more readable:

const ProductCard = ({ product, onAddToCart }) => {
  const priceDisplay = product.salePrice ? (
    <div className="price">
      <span className="sale-price">${product.salePrice}</span>
      <span className="original-price">${product.price}</span>
    </div>
  ) : (
    <div className="price">
      <span className="regular-price">${product.price}</span>
    </div>
  );

  const actionButton = product.inStock ? (
    <button onClick={() => onAddToCart(product.id)}>
      Add to Cart
    </button>
  ) : (
    <button disabled>Out of Stock</button>
  );

  return (
    <div className="product-card">
      <h3>{product.name}</h3>
      {priceDisplay}
      {actionButton}
    </div>
  );
};

6. Component Functions (Building Blocks)

// Functional component pattern I use most
const Welcome = ({ name, role = 'user' }) => {
  return (
    <div className="welcome">
      <h1>Hello, {name}!</h1>
      <Badge variant={role}>{role}</Badge>
    </div>
  );
};

// Usage
const App = () => (
  <div>
    <Welcome name="Alex" role="admin" />
    <Welcome name="Jordan" /> {/* role defaults to 'user' */}
  </div>
);

7. Inline Styles (When and How)

I use inline styles sparingly - mainly for dynamic values or CSS-in-JS libraries:

const ProgressBar = ({ progress, color = '#3b82f6' }) => {
  const barStyle = {
    width: `${Math.min(progress, 100)}%`,
    backgroundColor: color,
    transition: 'width 0.3s ease',
  };

  return (
    <div className="progress-container">
      <div className="progress-bar" style={barStyle} />
      <span className="progress-text">{progress}%</span>
    </div>
  );
};

💡 Performance note: Define style objects outside render if they don't change, to avoid recreation.

8. List Rendering (Keys and Performance)

Learn more about React Rendering List and Key

Mapping over arrays is daily React work. Here's how I handle common scenarios:

const TodoList = ({ todos, onToggle, onDelete }) => (
  <ul className="todo-list">
    {todos.map(todo => (
      <li key={todo.id} className={todo.completed ? 'completed' : ''}>
        <input
          type="checkbox"
          checked={todo.completed}
          onChange={() => onToggle(todo.id)}
        />
        <span>{todo.text}</span>
        <button onClick={() => onDelete(todo.id)}>Delete</button>
      </li>
    ))}
  </ul>
);

// Empty state handling
const TaskBoard = ({ tasks }) => (
  <div>
    {tasks.length > 0 ? (
      <ul>
        {tasks.map(task => (
          <TaskCard key={task.id} task={task} />
        ))}
      </ul>
    ) : (
      <EmptyState message="No tasks yet. Create your first task!" />
    )}
  </div>
);

⚠️ Always use stable, unique keys. Array indices work only for static lists.

9. JSX Comments (Documentation in Components)

const ComplexForm = () => (
  <div className="form-container">
    {/* User Info Section */}
    <div className="fieldset">
      <h3>Personal Information</h3>
      <input name="firstName" />
      <input name="lastName" />
    </div>
    
    {/* 
      TODO: Add validation for phone number format
      Currently accepts any string
    */}
    <input name="phone" type="tel" />
    
    {/* Submission disabled until validation passes */}
    <button type="submit" disabled={!isValid}>
      Submit
    </button>
  </div>
);

10. Fragments for Clean DOM (Avoiding Div Soup)

// ❌ Unnecessary wrapper div
const UserInfo = ({ user }) => (
  <div>
    <span>{user.name}</span>
    <span>{user.email}</span>
  </div>
);

// ✅ Fragment keeps DOM clean
const UserInfo = ({ user }) => (
  <>
    <span>{user.name}</span>
    <span>{user.email}</span>
  </>
);

// ✅ Useful for table rows, CSS Grid items, etc.
const TableRow = ({ data }) => (
  <>
    <td>{data.name}</td>
    <td>{data.value}</td>
    <td>{data.status}</td>
  </>
);

11. Spread Attributes (Props Forwarding)

Essential for building reusable components that extend native elements:

// Custom button that forwards all props
const Button = ({ variant = 'primary', children, ...rest }) => (
  <button 
    className={`btn btn-${variant}`}
    {...rest}
  >
    {children}
  </button>
);

// Usage - onClick, disabled, etc. are forwarded
<Button 
  variant="secondary" 
  onClick={handleClick}
  disabled={isLoading}
  aria-label="Save changes"
>
  {isLoading ? 'Saving...' : 'Save'}
</Button>

// Input wrapper example
const FormField = ({ label, error, ...inputProps }) => (
  <div className="field">
    <label>{label}</label>
    <input {...inputProps} className={error ? 'error' : ''} />
    {error && <span className="error-text">{error}</span>}
  </div>
);

12. Self-Closing Tags (Required for Empty Elements)

// ❌ This breaks in JSX
<img src="logo.png" alt="logo">
<br>
<input type="text">

// ✅ Self-close empty elements
<img src="logo.png" alt="logo" />
<br />
<input type="text" />

// ✅ Also works for custom components
<LoadingSpinner />
<Divider />
<UserAvatar userId={user.id} />

13. Nesting JSX Elements (Composition)

How I structure nested components for readability:

const ArticleCard = ({ article }) => (
  <article className="card">
    <header className="card-header">
      <h2>{article.title}</h2>
      <AuthorInfo author={article.author} />
      <PublishDate date={article.publishedAt} />
    </header>
    
    <div className="card-content">
      <p>{article.excerpt}</p>
      <TagList tags={article.tags} />
    </div>
    
    <footer className="card-footer">
      <LikeButton articleId={article.id} />
      <ShareButton url={article.url} />
      <BookmarkButton articleId={article.id} />
    </footer>
  </article>
);

14. Function Calls in JSX (Computed Values)

const formatCurrency = (amount) => 
  new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD'
  }).format(amount);

const getStatusColor = (status) => {
  const colors = {
    pending: 'orange',
    approved: 'green',
    rejected: 'red'
  };
  return colors[status] || 'gray';
};

const OrderSummary = ({ order }) => (
  <div className="order-summary">
    <h3>Order #{order.id}</h3>
    <p className="total">
      Total: {formatCurrency(order.total)}
    </p>
    <span 
      className="status-badge"
      style={{ backgroundColor: getStatusColor(order.status) }}
    >
      {order.status.toUpperCase()}
    </span>
  </div>
);

15. Falsy Values in JSX (What Renders and What Doesn't)

Understanding React's rendering behavior prevents bugs:

const ComponentWithFalsyValues = () => {
  const user = null;
  const count = 0;
  const isActive = false;
  const message = '';
  
  return (
    <div>
      {/* These render nothing: */}
      {null}
      {undefined}
      {false}
      {true}
      
      {/* These render their values: */}
      {0}        {/* Renders "0" */}
      {''}       {/* Renders empty string */}
      {NaN}      {/* Renders "NaN" */}
      
      {/* Safe patterns: */}
      {count > 0 && <p>Items: {count}</p>}
      {message && <p>{message}</p>}
      {user ? <UserProfile user={user} /> : <LoginPrompt />}
    </div>
  );
};

⚠️ Common bug: {items.length && <List />} renders "0" when array is empty. Use {items.length > 0 && <List />} instead.


Questions I Get About JSX

1. What is JSX in React?

JSX is a syntax extension that lets you write HTML-like code inside JavaScript. It compiles to React.createElement() calls, making it easier to describe component structure visually.

2. Can JSX be used without React?

JSX was designed for React, but other libraries like Preact and Solid.js support it too. You need a compiler (like Babel) configured for whichever library you're using.

3. Why does JSX require one parent element?

Functions can only return one value. Since JSX compiles to function calls, you need one root element. Fragments solve this without adding extra DOM nodes.

4. How is JSX compiled?

Babel transforms JSX into React.createElement() calls during build time. Modern React (17+) uses a new transform that doesn't require importing React in every file.

5. What's the difference between JSX and HTML?

JSX uses camelCase attributes (className, onClick), requires self-closing tags, supports JavaScript expressions in {}, and compiles to JavaScript function calls instead of being parsed as markup.

❤️ At Learn Virendana, we love creating high-quality React tutorials that simplify complex concepts and deliver a practical, real-world React learning experience for developers