DailyDevDiet

logo - dailydevdiet

Learn. Build. Innovate. Elevate your coding skills with dailydevdiet!

Chapter 4: Understanding JSX

Understanding JSX

JSX (JavaScript XML) is one of React’s most distinctive features and a cornerstone of writing React applications. It allows you to write HTML-like code directly within your JavaScript, providing a more intuitive way to describe UI components. This chapter will help you understanding JSX, its syntax, and how it works behind the scenes.

What is JSX?

JSX is a syntax extension for JavaScript that looks similar to HTML or XML. It was developed by Facebook specifically for React as a way to define what the UI should look like. While it resembles HTML, JSX is actually closer to JavaScript.

JSX Example

const element = <h1 className="greeting">Hello, world!</h1>;

This might look like HTML at first glance, but it’s actually JSX. When your application is built, this JSX is transformed into regular JavaScript function calls.

JSX vs HTML: Key Differences

While JSX looks like HTML, there are several important differences:

  1. Attributes use camelCase: In JSX, HTML attributes are written in camelCase rather than kebab-case.
// HTML: <div class="container" tabindex="0"></div>
// JSX:
<div className="container" tabIndex="0"></div>
  1. className instead of class: Since class is a reserved keyword in JavaScript, JSX uses className instead.
<div className="container"></div>
  1. Self-closing tags require a slash: In JSX, self-closing tags must include a closing slash.

// HTML: <img src="image.jpg">
// JSX:
<img src="image.jpg" />
  1. JavaScript expressions in curly braces: JSX allows you to embed JavaScript expressions by wrapping them in curly braces.
const name = 'John';
const element = <h1>Hello, {name}!</h1>;

How JSX Works Behind the Scenes

When you write JSX, it gets transformed into regular JavaScript code by tools like Babel before being executed by the browser. Let’s look at how this transformation works:

JSX Transformation

This JSX:

const element = <h1 className="greeting">Hello, world!</h1>;

Gets transformed into:

const element = React.createElement(
  'h1',
  { className: 'greeting' },
  'Hello, world!'
);

The React.createElement() function creates a JavaScript object called a “React element” that describes what should appear on the screen. This is why we need to import React at the top of files that use JSX, even if React isn’t directly referenced in the code.

The Structure of React Elements

When React.createElement() is called, it produces an object that looks something like this:

// Simplified version of what React.createElement() returns
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  }
};

React uses these objects to build and maintain the DOM.

Writing JSX

Let’s explore how to write JSX code for different scenarios:

Basic JSX Syntax

JSX elements can be simple:

const element = <div>Hello, world!</div>;

Or they can contain multiple nested elements. When using multiple lines, wrap the JSX in parentheses to avoid automatic semicolon insertion issues:

const element = (
  <div>
    <h1>Hello!</h1>
    <p>This is a paragraph.</p>
  </div>
);

Root Element Requirement

JSX expressions must have exactly one root element. To return multiple elements without an extra wrapper, use React Fragments:

// Using explicit Fragment syntax
const element = (
  <React.Fragment>
    <h1>Title</h1>
    <p>Paragraph</p>
  </React.Fragment>
);

// Using short syntax (empty tags)
const element = (
  <>
    <h1>Title</h1>
    <p>Paragraph</p>
  </>
);

JavaScript Expressions in JSX

You can embed any JavaScript expression in JSX by wrapping it in curly braces {}:

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

// You can use expressions in attributes too
const imageUrl = 'https://example.com/image.jpg';
const imgElement = <img src={imageUrl} alt="Example" />;

// You can even call functions
function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = { firstName: 'John', lastName: 'Doe' };
const greeting = <h1>Hello, {formatName(user)}!</h1>;

JSX is an Expression Too

Since JSX compiles into JavaScript function calls, you can use JSX wherever you can use expressions:

// In conditionals
function getGreeting(user) {
  if (user) {
    return <h1>Hello, {user.name}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}

// In variables
const userGreeting = <h1>Welcome back!</h1>;
const guestGreeting = <h1>Please sign up.</h1>;

// In function returns
function Greeting(props) {
  if (props.isLoggedIn) {
    return userGreeting;
  }
  return guestGreeting;
}

Multiline JSX

For readability, multiline JSX should be wrapped in parentheses:

const element = (
  <div>
    <h1>Hello!</h1>
    <p>Welcome to JSX.</p>
  </div>
);

JSX Attributes

JSX attributes are written using camelCase convention and can accept string literals or JavaScript expressions.

String Literals

const element = <div className="container">Hello</div>;

JavaScript Expressions

const isActive = true;
const element = <div className={isActive ? 'active' : 'inactive'}>Item</div>;

Spread Attributes

You can use the spread operator to pass an entire props object:

const props = { firstName: 'John', lastName: 'Doe' };
const element = <Greeting {...props} />;

Boolean Attributes

For boolean attributes, including just the name implies true:

// These are equivalent:
<input disabled={true} />
<input disabled />

JSX Children

JSX elements can contain child elements, just like HTML:

const element = (
  <div>
    <h1>Title</h1>
    <p>Paragraph</p>
  </div>
);

String Literals as Children

const element = <div>Hello, world!</div>;

JavaScript Expressions as Children

const element = <div>{2 + 2}</div>; // Renders "4"

Functions as Children

function Item(props) {
  return <li>{props.message}</li>;
}

function List() {
  const items = ['Apple', 'Banana', 'Cherry'];
  return (
    <ul>
      {items.map((item, index) => (
        <Item key={index} message={item} />
      ))}
    </ul>
  );
}

Conditional Rendering in JSX

There are several ways to render content conditionally in JSX:

If Statements (Outside JSX)

function Greeting(props) {
  if (props.isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

Inline Logical && Operator

function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          You have {unreadMessages.length} unread messages.
        </h2>
      }
    </div>
  );
}

Ternary Operator

function UserStatus(props) {
  return (
    <div>
      The user is {props.isLoggedIn ? 'logged in' : 'logged out'}.
    </div>
  );

Preventing Component Rendering

function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    <div className="warning">
      Warning!
    </div>
  );
}

Lists and Keys in JSX

When rendering lists in React, you need to provide a unique “key” for each item:

function NumberList(props) {
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number) => (
        <li key={number.toString()}>
          {number}
        </li>
      ))}
    </ul>
  );
}

Rules for Keys

  1. Keys must be unique among siblings (not globally)
  2. Use stable IDs when available (e.g., database IDs)
  3. Use indices as a last resort (can cause issues with reordering)
// Good (unique IDs):
{items.map(item => <ListItem key={item.id} item={item} />)}

// Less ideal (indices):
{items.map((item, index) => <ListItem key={index} item={item} />)}

JSX with CSS

There are several ways to include CSS with JSX:

1. CSS Classes

import './Button.css'; // Import the CSS file

function Button() {
  return <button className="primary-button">Click Me</button>;
}import './Button.css'; // Import the CSS file

function Button() {
  return <button className="primary-button">Click Me</button>;
}

2. Inline Styles

Inline styles in JSX use JavaScript objects with camelCase properties:

function Button() {
  const buttonStyle = {
    backgroundColor: 'blue',
    color: 'white',
    padding: '10px 15px',
    borderRadius: '4px',
    border: 'none',
    cursor: 'pointer'
  };

  return <button style={buttonStyle}>Click Me</button>
  }

Or inline:

function Button() {
  return (
    <button
      style={{
        backgroundColor: 'blue',
        color: 'white'
      }}
    >
      Click Me
    </button>
  );
}

3. CSS Modules

CSS Modules scope CSS locally by default:

/* Button.module.css */
.button {
  background-color: blue;
  color: white;
}
import styles from './Button.module.css';

function Button() {
  return <button className={styles.button}>Click Me</button>;
}

4. Styled Components

Using a library like styled-components:

import styled from 'styled-components';

const StyledButton = styled.button`
  background-color: blue;
  color: white;
  padding: 10px 15px;
  border-radius: 4px;
  border: none;
  cursor: pointer;
`;

function Button() {
  return <StyledButton>Click Me</StyledButton>;
}

JSX Gotchas and Best Practices

1. Comments in JSX

JSX comments must be wrapped in curly braces:

const element = (
  <div>
    {/* This is a comment in JSX */}
    <h1>Hello, world!</h1>
  </div>
);

2. HTML Entities

HTML entities can be used directly in JSX text:

const element = <div>First&middot Second</div>;

Or via Unicode characters:

const element = <div>First · Second</div>;

3. Avoiding XSS with JSX

React automatically escapes values embedded in JSX to prevent cross-site scripting (XSS) attacks:

const userInput = '<script>alert("XSS attack!")</script>';
// This is safe, React will escape the content
const element = <div>{userInput}</div>;

This renders the string as text, not as HTML.

4. Dangerous HTML Rendering

In rare cases when you need to render HTML content, you can use dangerouslySetInnerHTML (but use with caution!):

function createMarkup() {
  return {__html: '<div>HTML content</div>'};
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}

5. Custom HTML Attributes

For non-standard HTML attributes, use the data- prefix:

<div data-custom-attribute="value" />

Or for completely custom components, any prop name is valid:

<MyComponent customAttribute="value" />

JSX Alternatives

While JSX is the preferred way to write React components, it’s not required:

Using React Without JSX

// With JSX:
const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);
// Without JSX (equivalent code):
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

Advanced JSX Patterns

1. Dynamic Tag Names

You can use variables for component or element types:

function Story(props) {
  // Capitalize variable names for components
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}

2. Render Props Pattern

function MouseTracker() {
  return (
    <Mouse render={mouse => (
      <p>The mouse position is {mouse.x}, {mouse.y}</p>
    )}/>
  );
}

3. JSX Spread Attributes with Exclusion

function Button({ className, ...props }) {
  return (
    <button
      className={`default-button ${className}`}
      {...props}
    />
  );
}

Practical JSX Examples

1. A Form Component

function ContactForm() {
  const [name, setName] = React.useState('');
  const [email, setEmail] = React.useState('');
  const [message, setMessage] = React.useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({ name, email, message });
  };

  return (
    <form onSubmit={handleSubmit} className="contact-form">
      <div className="form-group">
        <label htmlFor="name">Name:</label>
        <input
          type="text"
          id="name"
          value={name}
          onChange={(e) => setName(e.target.value)}
          required
        />
      </div>
     
      <div className="form-group">
        <label htmlFor="email">Email:</label>
        <input
          type="email"
          id="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
        />
      </div>
     
      <div className="form-group">
        <label htmlFor="message">Message:</label>
        <textarea
          id="message"
          value={message}
          onChange={(e) => setMessage(e.target.value)}
          required
          rows="5"
        ></textarea>
      </div>
     
      <button type="submit" className="submit-button">
        Send Message
      </button>
    </form>
  );
}

2. A Card Component

function Card({ title, description, imageUrl, tags = [] }) {
  return (
    <div className="card">
      {imageUrl && (
        <div className="card-image">
          <img src={imageUrl} alt={title} />
        </div>
      )}
     
      <div className="card-content">
        <h2 className="card-title">{title}</h2>
        <p className="card-description">{description}</p>
       
        {tags.length > 0 && (
          <div className="card-tags">
            {tags.map((tag, index) => (
              <span key={index} className="tag">
                {tag}
              </span>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

3. A Navigation Menu

function Navigation({ links, activeLink }) {
  return (
    <nav className="main-navigation">
      <ul className="nav-list">
        {links.map((link) => (
          <li key={link.id} className="nav-item">
            <a
              href={link.url}
              className={`nav-link ${activeLink === link.id ? 'active' : ''}`}
            >
              {link.icon && <span className="nav-icon">{link.icon}</span>}
              <span className="nav-text">{link.text}</span>
            </a>
          </li>
        ))}
      </ul>
    </nav>
  );
}

Debugging JSX

When working with JSX, common errors include:

  1. Missing closing tags: All JSX elements must be closed.
  2. Case sensitivity: JSX components must start with a capital letter.
  3. Runtime errors: Check the console for detailed error messages.
  4. Incorrect component imports: Ensure you’re importing all needed components.

Using React Developer Tools

The React Developer Tools browser extension helps with debugging JSX:

  1. Inspect component hierarchy
  2. View props and state of each component
  3. Track component re-renders
  4. Debug performance issues

Summary

JSX is a powerful syntax extension for JavaScript that makes React code more readable and intuitive. In this chapter, we’ve covered:

  • What JSX is and how it differs from HTML
  • How JSX works behind the scenes with React.createElement()
  • Basic JSX syntax rules and patterns
  • Working with expressions, attributes, and children in JSX
  • Conditional rendering patterns
  • List rendering and the importance of keys
  • Different ways to include CSS with JSX
  • Common gotchas and best practices
  • Advanced JSX patterns and practical examples

JSX bridges the gap between JavaScript logic and UI, allowing you to write more expressive React components. Though it might seem like a syntax oddity at first, JSX quickly becomes natural and preferable for React development.

In the next chapter, we’ll dive into React Components, building on our understanding of JSX to create reusable UI building blocks.

Further Reading

Scroll to Top