JSX Syntax and Expression Integration

Understanding React's powerful markup extension

What is JSX?

JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code within your JavaScript. It's one of React's most distinctive features and a key part of what makes React development so intuitive.

JSX Example

// This is JSX
const element = <h1>Hello, world!</h1>;

Equivalent JavaScript without JSX

// This is what the above JSX compiles to
const element = React.createElement('h1', null, 'Hello, world!');

JSX provides a familiar syntax for defining what your UI should look like. If you know HTML, you already understand much of JSX. However, JSX is more powerful because it allows you to embed JavaScript expressions directly in your markup.

Analogy: JSX as a Blueprint Language

Think of JSX as a specialized blueprint language for architects:

  • Regular HTML is like a basic floor plan drawn on paper - static and limited
  • JSX is like an interactive 3D architectural model where you can not only visualize the structure but also define how it responds to different conditions (time of day, weather, number of occupants)
  • Just as architects would find it more intuitive to design in a visual language rather than writing specs in plain text, developers find it more intuitive to define UI structures in JSX rather than raw JavaScript function calls

Why JSX?

While React doesn't require JSX, it offers several significant advantages:

graph TD A[JSX Code] --> B[Babel Transpiler] B --> C[JavaScript] C --> D[Browser] subgraph "Developer Benefits" E[Familiar Syntax] F[Clearer UI Structure] G[Compile-time Error Checking] H[JavaScript Integration] end

Facebook developed JSX for React because they believed that separating technologies (like templates and logic) creates artificial separation, while separating concerns (like UI components) creates more maintainable code.

JSX Fundamentals

Element Syntax

Basic Element

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

Self-Closing Element

const image = <img src="profile.jpg" alt="Profile" />;

Nested Elements

const article = (
  <article>
    <h2>Article Title</h2>
    <p>This is a paragraph of text.</p>
  </article>
);

Important JSX Rules

  • JSX elements must be wrapped in a single parent element (or fragment)
  • All tags must be closed (either with closing tag or self-closing syntax)
  • JSX attributes use camelCase naming convention (e.g., className instead of class)

JSX vs HTML: Key Differences

HTML JSX Reason
class="container" className="container" "class" is a reserved keyword in JavaScript
for="email" htmlFor="email" "for" is a reserved keyword in JavaScript
onclick="handleClick()" onClick={handleClick} Camel case & passing functions as references
tabindex="0" tabIndex={0} Camel case for consistency
style="color: red;" style={{ color: 'red' }} JSX uses JavaScript objects for styles

JavaScript Expressions in JSX

One of the most powerful features of JSX is the ability to embed JavaScript expressions directly in your markup. This enables dynamic content and logic within your components.

Embedding Variables

const name = 'John';
const greeting = <h1>Hello, {name}!</h1>;

Embedding Expressions

const product = { name: 'Laptop', price: 999.99 };
const productInfo = (
  <div>
    <h2>{product.name}</h2>
    <p>Price: ${product.price.toFixed(2)}</p>
    <p>With tax: ${(product.price * 1.08).toFixed(2)}</p>
  </div>
);

Using Arrays

const colors = ['Red', 'Green', 'Blue'];
const colorList = (
  <ul>
    {colors.map(color => <li key={color}>{color}</li>)}
  </ul>
);

What Can Go Inside Curly Braces

You can put any valid JavaScript expression inside the curly braces in JSX. This includes:

What You Cannot Put in JSX Expressions

  • Flow control statements like if, for, while
  • Variable declarations with var, let, or const
  • Regular function or class declarations

For these cases, you need to use approaches like ternary operators, array methods, or write your logic outside the JSX.

Conditional Rendering in JSX

Since JSX is just JavaScript, we can use JavaScript's conditional operators to render different elements based on conditions.

Ternary Operator

function Greeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn 
        ? <h1>Welcome back!</h1> 
        : <h1>Please sign in.</h1>}
    </div>
  );
}

Logical && Operator

function Notifications({ messages }) {
  return (
    <div>
      {messages.length > 0 && (
        <h2>You have {messages.length} unread messages.</h2>
      )}
    </div>
  );
}

Immediately-Invoked Function Expressions (IIFE)

For more complex conditional logic:

function WeatherInfo({ temperature }) {
  return (
    <div>
      {(() => {
        if (temperature < 32) return <p>It's freezing!</p>;
        if (temperature < 70) return <p>It's cool.</p>;
        return <p>It's warm!</p>;
      })()}
    </div>
  );
}

Pre-processing Outside JSX

Often the cleanest approach for complex conditions:

function WeatherInfo({ temperature }) {
  let message;
  
  if (temperature < 32) {
    message = <p>It's freezing!</p>;
  } else if (temperature < 70) {
    message = <p>It's cool.</p>;
  } else {
    message = <p>It's warm!</p>;
  }
  
  return (
    <div>
      {message}
    </div>
  );
}

Real-World Example: Conditional UI Elements

Consider a product listing where premium products have a special badge:

function ProductCard({ product }) {
  return (
    <div className="product-card">
      <img src={product.image} alt={product.name} />
      
      {/* Premium badge shown conditionally */}
      {product.isPremium && (
        <span className="premium-badge">Premium</span>
      )}
      
      <h3>{product.name}</h3>
      
      {/* Different price display based on sale status */}
      {product.onSale ? (
        <div className="price-container">
          <span className="regular-price">${product.regularPrice.toFixed(2)}</span>
          <span className="sale-price">${product.salePrice.toFixed(2)}</span>
        </div>
      ) : (
        <div className="price-container">
          <span className="regular-price">${product.regularPrice.toFixed(2)}</span>
        </div>
      )}
      
      {/* Availability message */}
      {product.inStock ? (
        <span className="in-stock">In Stock</span>
      ) : (
        <span className="out-of-stock">Out of Stock</span>
      )}
    </div>
  );
}

Lists and Keys in JSX

When rendering lists of elements in React, we typically use array methods like map() to transform data into JSX elements. React requires a special key prop to efficiently update the UI when items change.

Basic List Rendering

function FruitList() {
  const fruits = ['Apple', 'Banana', 'Cherry', 'Durian'];
  
  return (
    <ul>
      {fruits.map(fruit => (
        <li key={fruit}>{fruit}</li>
      ))}
    </ul>
  );
}

Keys and Why They Matter

Keys help React identify which items have changed, are added, or are removed. Keys should be unique among siblings.

graph TD subgraph "Without Keys" A1["React"] -- "Difficult to track changes" --> B1["List Items"] end subgraph "With Keys" A2["React"] -- "Track item #1" --> B2["Item key=1"] A2 -- "Track item #2" --> C2["Item key=2"] A2 -- "Track item #3" --> D2["Item key=3"] end

Rules for Keys

  • Keys must be unique among siblings (not globally)
  • Stable identifiers (like IDs) are ideal for keys
  • Array indices can be used as a last resort, but can cause issues if items reorder
  • Keys don't get passed to components as props (use a different prop name if needed)

List Rendering with Complex Data

function UserList({ users }) {
  return (
    <div className="user-list">
      {users.map(user => (
        <div key={user.id} className="user-card">
          <img src={user.avatar} alt={`${user.name}'s avatar`} />
          <h3>{user.name}</h3>
          <p>{user.email}</p>
          <p>Joined: {new Date(user.joinDate).toLocaleDateString()}</p>
        </div>
      ))}
    </div>
  );
}

Analogy: Keys as Name Tags

Think of React's keys like name tags at a conference:

  • Without name tags, if people moved positions, you wouldn't know who was who at a glance
  • With name tags, even if everyone rearranges themselves, you can still identify each person quickly
  • Just as using duplicate name tags would be confusing at a conference, duplicate keys confuse React
  • Just as unique IDs (like employee numbers) are better for identification than descriptive names (which might have duplicates), stable IDs make better keys than content values

JSX Attributes and Props

JSX elements can have attributes similar to HTML elements, but with some important differences. In React, these attributes are called "props" when they're passed to components.

HTML Attributes in JSX

// Basic attributes
const link = <a href="https://reactjs.org" target="_blank">React Website</a>;

// className instead of class
const container = <div className="container">Content</div>;

// htmlFor instead of for
const label = <label htmlFor="username">Username:</label>;

Dynamic Attributes

const imageUrl = "profile.jpg";
const username = "johndoe";

// Using variables in attributes
const profileImage = <img src={imageUrl} alt={`${username}'s profile`} />;

// Conditional attributes
const button = <button disabled={!isActive}>Click Me</button>;

Spreading Attributes

When you have an object containing multiple properties that you want to pass as props:

const buttonProps = {
  disabled: false,
  className: 'primary-button',
  onClick: handleClick
};

// Using the spread operator
const button = <button {...buttonProps}>Click Me</button>;

Custom Attributes with data-*

// Custom data attributes work the same as in HTML
const listItem = <li data-testid="item" data-category="fruit">Apple</li>;

Styling with JSX

Using className

// Using CSS classes
const header = <header className="app-header dark-theme">My App</header>;

Inline Styles

// Inline styles use objects with camelCase properties
const styles = {
  backgroundColor: 'black',
  color: 'white',
  padding: '10px',
  borderRadius: '4px'
};

const button = <button style={styles}>Dark Button</button>;

// Inline style object directly in JSX
const inlineButton = (
  <button 
    style={{
      backgroundColor: 'blue',
      color: 'white',
      padding: '10px'
    }}
  >
    Blue Button
  </button>
);

CSS-in-JS Libraries

Many React projects use CSS-in-JS libraries like styled-components or emotion for more powerful styling options:

// Using styled-components
import styled from 'styled-components';

const Button = styled.button`
  background-color: ${props => props.primary ? 'blue' : 'gray'};
  color: white;
  padding: 10px 15px;
  border-radius: 4px;
  border: none;
  cursor: pointer;
  
  &:hover {
    opacity: 0.8;
  }
`;

// Usage
const StyledButtonExample = () => (
  <div>
    <Button primary>Primary Button</Button>
    <Button>Secondary Button</Button>
  </div>
);

JSX Fragments

React requires that JSX elements have a single parent. But sometimes you don't want to add an extra DOM element just for this purpose. React Fragments solve this problem.

The Problem with Wrapper Elements

// Adding extra div just for JSX syntax requirements
function ListItems() {
  return (
    <div> {/* Extra div that might break styling */}
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </div>
  );
}

Solution: React.Fragment

import React from 'react';

function ListItems() {
  return (
    <React.Fragment>
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </React.Fragment>
  );
}

Shorthand Fragment Syntax

function ListItems() {
  return (
    <>
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </>
  );
}

Fragments with Keys

When using fragments in a list, you might need to assign keys. In this case, you can't use the shorthand syntax:

function GroceryList({ items }) {
  return (
    <dl>
      {items.map(item => (
        // Can't use <></> here because we need to specify a key
        <React.Fragment key={item.id}>
          <dt>{item.name}</dt>
          <dd>{item.description}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

Real-World Use Case: Table Rows

Fragments are particularly useful for tables, where you can't have divs between tr elements:

function UserTable({ users }) {
  return (
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Email</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        {users.map(user => (
          <tr key={user.id}>
            <td>{user.id}</td>
            <td>{user.name}</td>
            <td>{user.email}</td>
            <td>
              <>
                <button onClick={() => editUser(user.id)}>Edit</button>
                <button onClick={() => deleteUser(user.id)}>Delete</button>
              </>
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

JSX Comments

Comments in JSX need to be placed inside curly braces, using JavaScript's comment syntax.

function CommentExample() {
  return (
    <div>
      {/* This is a comment in JSX */}
      <h1>Hello World</h1>
      
      {/* 
        Multi-line comments
        work too!
      */}
      
      {/* You can put expressions in comments: {2 + 2} */}
      
      {/* 
        JSX-specific gotchas:
        - Don't use HTML comments: <!-- This doesn't work -->
        - Make sure to close your braces: { /* This causes an error  }
      */}
    </div>
  );
}

JSX Under the Hood

To truly understand JSX, it's important to know what's happening behind the scenes. JSX is ultimately transpiled into regular JavaScript before it runs in the browser.

JSX Code const element = <h1>Hello</h1>; JavaScript const element = React.createElement( 'h1', null, 'Hello'); Complex JSX <div className="container"> <h1>{title}</h1> </div> Complex JavaScript React.createElement('div', { className: 'container' }, React.createElement('h1', null, title) ); Babel Babel

The React.createElement Function

When Babel transpiles JSX, it converts it to calls to React.createElement().

// The React.createElement() function takes 3 arguments:
React.createElement(
  type,      // Either a string (for DOM elements) or a component
  props,     // An object of properties/attributes or null
  ...children // The content inside the element
);

// Example transformations:
// JSX:
<button className="btn" onClick={handleClick}>
  Click me
</button>

// Transpiled JavaScript:
React.createElement(
  'button',
  { className: 'btn', onClick: handleClick },
  'Click me'
);

// JSX with children:
<div>
  <h1>Title</h1>
  <p>Paragraph</p>
</div>

// Transpiled JavaScript:
React.createElement(
  'div', 
  null, 
  React.createElement('h1', null, 'Title'),
  React.createElement('p', null, 'Paragraph')
);

Analogy: Architectural Blueprints

Think of the relationship between JSX and React.createElement() like this:

  • JSX is like an architectural blueprint with intuitive visual representation of the structure
  • React.createElement() is like the technical specifications and measurements - same information but in a format that's harder for humans to visualize
  • Babel is the architect who translates between the visual plans and the technical specs

While technically you could write applications using just React.createElement() calls, it would be like trying to design a building using only numerical measurements without diagrams - possible, but much more difficult to visualize and maintain.

Why It Matters

Understanding this transformation helps explain:

React 17+ JSX Transformation

React 17 introduced a new JSX transformation that doesn't require importing React just to use JSX:

// Before React 17 (old transform)
import React from 'react';
function App() {
  return <h1>Hello World</h1>;
}

// After React 17 (new transform)
// No import needed for JSX!
function App() {
  return <h1>Hello World</h1>;
}

The new transform uses functions from a new package called `react/jsx-runtime` that is automatically imported by the compiler.

JSX Best Practices

graph TD A[JSX Best Practices] A --> B[Keep Components Small] A --> C[Use Meaningful Names] A --> D[Extract Repeated Logic] A --> E[Single Responsibility] A --> F[Consistent Formatting] A --> G[Use Fragment Shorthand] A --> H[Destructure Props] B --> B1[Easier to understand and test] C --> C1[Self-documenting code] D --> D1[Avoid duplication] E --> E1[Components should do one thing well] F --> F1[Use Prettier] G --> G1[Less visual noise] H --> H1[Cleaner component code]

Organization and Readability

Use parentheses for multi-line JSX

// Good - easier to read with parentheses
return (
  <div>
    <h1>Title</h1>
    <p>Content</p>
  </div>
);

// Bad - hard to read without parentheses
return <div>
  <h1>Title</h1>
  <p>Content</p>
</div>;

Destructure props for clarity

// Good - destructured props
function UserProfile({ name, email, avatar }) {
  return (
    <div>
      <img src={avatar} alt={name} />
      <h2>{name}</h2>
      <p>{email}</p>
    </div>
  );
}

// Less clear
function UserProfile(props) {
  return (
    <div>
      <img src={props.avatar} alt={props.name} />
      <h2>{props.name}</h2>
      <p>{props.email}</p>
    </div>
  );
}

Logic and Structure

Extract complex logic from JSX

// Good - complex logic extracted
function ProductGrid({ products, filter }) {
  // Complex filtering logic extracted from JSX
  const filteredProducts = useMemo(() => {
    return products
      .filter(product => product.category === filter.category)
      .filter(product => product.price <= filter.maxPrice)
      .sort((a, b) => a.price - b.price);
  }, [products, filter]);
  
  return (
    <div className="grid">
      {filteredProducts.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

// Bad - complex logic inside JSX
function ProductGrid({ products, filter }) {
  return (
    <div className="grid">
      {products
        .filter(product => product.category === filter.category)
        .filter(product => product.price <= filter.maxPrice)
        .sort((a, b) => a.price - b.price)
        .map(product => (
          <ProductCard key={product.id} product={product} />
        ))
      }
    </div>
  );
}

Use logical && operator carefully

// Potential bug - if count is 0, renders "0" instead of nothing
function Notifications({ count }) {
  return (
    <div>
      {count && <p>You have {count} notifications</p>}
    </div>
  );
}

// Better approach - be explicit
function Notifications({ count }) {
  return (
    <div>
      {count > 0 && <p>You have {count} notifications</p>}
    </div>
  );
}

Performance Considerations

Avoid creating functions in JSX

// Bad - creates a new function on every render
function Button({ text, id }) {
  return (
    <button onClick={() => console.log(`Button ${id} clicked`)}>
      {text}
    </button>
  );
}

// Better - define the function outside JSX
function Button({ text, id }) {
  const handleClick = () => {
    console.log(`Button ${id} clicked`);
  };
  
  return (
    <button onClick={handleClick}>
      {text}
    </button>
  );
}

Use keys properly

// Bad - using index as key
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={index}>{todo.text}</li>
      ))}
    </ul>
  );
}

// Good - using stable ID as key
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

Real-World JSX Example: Building a Dashboard Card

Let's put together what we've learned by building a realistic UI component using JSX. We'll create a dashboard card that displays different information based on its type.

import React from 'react';
import './DashboardCard.css';

function DashboardCard({ 
  type, 
  title, 
  data, 
  icon, 
  trend, 
  loading, 
  error 
}) {
  // Handle different loading/error states
  if (loading) {
    return (
      <div className="dashboard-card dashboard-card--loading">
        <div className="loading-spinner"></div>
      </div>
    );
  }
  
  if (error) {
    return (
      <div className="dashboard-card dashboard-card--error">
        <div className="error-icon">⚠️</div>
        <p>{error}</p>
      </div>
    );
  }
  
  // Format the data based on card type
  const formattedData = formatCardData(type, data);
  
  // Determine trend class and icon
  const trendClass = trend > 0 
    ? 'trend--positive' 
    : trend < 0 
      ? 'trend--negative' 
      : 'trend--neutral';
      
  const trendIcon = trend > 0 
    ? '↑' 
    : trend < 0 
      ? '↓' 
      : '→';
  
  return (
    <div className={`dashboard-card dashboard-card--${type}`}>
      <div className="card-header">
        <h3 className="card-title">{title}</h3>
        {icon && <div className="card-icon">{icon}</div>}
      </div>
      
      <div className="card-content">
        {/* Different content based on card type */}
        {type === 'stat' && (
          <>
            <div className="stat-value">{formattedData.value}</div>
            {trend !== undefined && (
              <div className={`trend ${trendClass}`}>
                {trendIcon} {Math.abs(trend)}%
              </div>
            )}
          </>
        )}
        
        {type === 'list' && (
          <ul className="data-list">
            {formattedData.items.map(item => (
              <li key={item.id} className="data-list-item">
                <span className="item-label">{item.label}</span>
                <span className="item-value">{item.value}</span>
              </li>
            ))}
          </ul>
        )}
        
        {type === 'chart' && (
          <div className="chart-container">
            {/* Chart component would go here */}
            {formattedData.chartComponent}
          </div>
        )}
      </div>
      
      {/* Optional footer */}
      {formattedData.footer && (
        <div className="card-footer">
          {formattedData.footer}
        </div>
      )}
    </div>
  );
}

// Helper function to format card data
function formatCardData(type, data) {
  switch (type) {
    case 'stat':
      return {
        value: typeof data === 'number' 
          ? data.toLocaleString() 
          : data
      };
    case 'list':
      return {
        items: data.map(item => ({
          id: item.id,
          label: item.label,
          value: typeof item.value === 'number' 
            ? item.value.toLocaleString() 
            : item.value
        }))
      };
    case 'chart':
      return {
        chartComponent: data.component,
        footer: data.footer
      };
    default:
      return { value: data };
  }
}

export default DashboardCard;

Using the Component

import React from 'react';
import DashboardCard from './DashboardCard';
import SalesChart from './SalesChart';

function Dashboard() {
  return (
    <div className="dashboard">
      {/* Stats Card */}
      <DashboardCard
        type="stat"
        title="Total Revenue"
        data={42589.75}
        icon="💰"
        trend={5.4}
      />
      
      {/* List Card */}
      <DashboardCard
        type="list"
        title="Top Products"
        data={[
          { id: 1, label: 'Product A', value: 12345 },
          { id: 2, label: 'Product B', value: 8765 },
          { id: 3, label: 'Product C', value: 5432 }
        ]}
        icon="📊"
      />
      
      {/* Chart Card */}
      <DashboardCard
        type="chart"
        title="Sales Trend"
        data={{
          component: <SalesChart data={salesData} />,
          footer: "Last 30 days"
        }}
      />
      
      {/* Loading State */}
      <DashboardCard
        type="stat"
        title="Visitors"
        loading={true}
      />
      
      {/* Error State */}
      <DashboardCard
        type="stat"
        title="Conversion Rate"
        error="Failed to load data"
      />
    </div>
  );
}

export default Dashboard;

This example demonstrates many JSX concepts:

Practice Activities

Activity 1: JSX Conversion

Objective: Practice converting between HTML and JSX.

Instructions:

  1. Take the following HTML snippet and convert it to valid JSX:
<!-- Convert this HTML to JSX -->
<div class="user-profile">
  <img src="profile.jpg" alt="User Profile" class="profile-image">
  <div class="user-details">
    <h2>John Doe</h2>
    <p>Web Developer</p>
    <label for="user-status">Status:</label>
    <select id="user-status">
      <option value="active">Active</option>
      <option value="away">Away</option>
    </select>
    <button onclick="editProfile()">Edit Profile</button>
  </div>
</div>

Activity 2: Dynamic JSX

Objective: Practice using JavaScript expressions in JSX.

Instructions:

  1. Create a component called ProductCard that receives the following props:
    • product: An object with properties: id, name, price, discountPercentage, and inStock
  2. The component should:
    • Display the product name as a heading
    • If there's a discount, show both the original price (struck through) and the discounted price
    • If there's no discount, just show the price
    • Show "In Stock" in green or "Out of Stock" in red based on the inStock property
    • Include a "Add to Cart" button that is disabled if the product is out of stock

Activity 3: List Rendering

Objective: Practice rendering lists with JSX and keys.

Instructions:

  1. Create a component called TaskList that:
    • Accepts an array of task objects with properties: id, text, priority, and completed
    • Renders the tasks as a list of items
    • Color-codes tasks by priority (high: red, medium: orange, low: green)
    • Strikes through completed tasks
    • Includes a summary showing counts of completed and remaining tasks
  2. Ensure proper use of keys and conditional rendering

Resources for Further Learning

Official Documentation:

Interactive Learning:

Additional Resources:

Summary

In the next lecture, we'll dive into React components, understanding both functional and class components and how they work with props.