Title Image - React Essentials II: A Practical Guide to JSX

React Essentials II: A Practical Guide to JSX

Yashmeet Singh · · 12 minute read

Introduction

I recently showed you how to create a React app using Vite. Today, you’ll learn about JSX, a potent mix of JavaScript and HTML that’s used to build interactive React applications.

Here’s what we’ll cover in this article:

  • Basic JSX syntax and rules.
  • How JSX allows you to embed JavaScript variables with HTML tags.
  • Conditional rendering using ternary operator and Logical AND.
  • How to iterate over and render elements of an array.
  • Apply inline styles and CSS classes to JSX components.
  • Handle JSX events with custom methods.

We’d appreciate JSX better if we understood one of the core React functions. Let’s take a look at it first.

React.createElement()

React is a JavaScript library that enables us to build interactive user interfaces (UIs). Its fundamental building blocks are components that return React elements. You can create such elements using React’s createElement() method.

Let’s see this method in action.

Suppose we want to build a component to represent a college course. We’ll keep it simple and only display the text Course Page using an html header tag.

Here’s one way to do this:

  • Create a functional component named Course.
  • Import the React library and create an element using the createElement() with the below arguments:
    • h2: The HTML tag we want to render.
    • null: The 2nd argument is used to pass props. We don’t need any props, so keep it null.
    • Course Page: The inner text of the h2 tag.
  • Return the element from the functional component:
import React from "react";
 
export default function Course() {
  return React.createElement("h2", null, "Course Page");
}

Why JSX?

You could build UIs using the createElement() method as above. However, complex UIs require a large number of elements, including nested elements. Writing such UI using the createElement() method will be extremely tedious.

That’s why React allows components to return JSX code, which combines the familiar syntax of HTML with JavaScript expressions1.

For example, we can replace the React.createElement() call in the Course component with the HTML tag <h2>Course Page</h2>. You don’t have to import the React library as well:

export default function Course() {
  return <h2>Course Page</h2>;
}

Now that you know why JSX is popular, I’ll show you how to use it with practical examples.

Nested Elements

Let’s add some meat to our Course component. We’ll add the course title and instructor using header tags and wrap them with a div:

export default function Course() {
  return (
    <div>
      <h2>Course: Statistics</h2>
      <h3>Instructor: Monica Gill</h3>
    </div>
  );
}

Notice the parentheses surrounding the entire return value - they allow you to break nested or long lines of JSX code into multiple lines for better readability.

JSX as a JavaScript Variable

You can assign the JSX expression to a JavaScript variable and then use that variable in the component’s return statement:

export default function Course() {
 
  const courseDetails = (
    <div>
      <h2>Course: Statistics</h2>
      <h3>Instructor: Monica Gill</h3>
    </div>
  );
 
  return courseDetails;
}

Single Top-Level Element

One odd quirk about JSX is that any JSX expression can only have one top-level element.

Suppose you remove the div around the header tags. You’ll have two elements (h2 & h3) at the top level, and that’ll result in an error:

export default function Course() {
  return (
      <h2>Course: Statistics</h2>
      <h3>Instructor: Monica Gill</h3>
  );
}

[plugin:vite:react-babel] /home/learn/react/react-from-scratch/src/Course.jsx: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>…</>? (4:6).

What can you do if you want to avoid introducing an additional div?

As the error message suggests, you can use a JSX fragment (<>…</>) to wrap your JSX code. It’ll get rid of the error without adding any unnecessary HTML elements:

export default function Course() {
  return (
    <>
      <h2>Course: Statistics</h2>
      <h3>Instructor: Monica Gill</h3>
    </>
  );
}

All Tags Must be Closed

JSX code may look like standard HTML, but it’s much stricter. Unlike HTML, JSX requires all elements or tags to be closed properly.

For example, HTML allows you to use non-closing <br> to add a line break. Doing so in JSX will result in an error:

export default function Course() {
  return (
    <>
      <h2>Course: Statistics</h2>
      <br>
      <h3>Instructor: Monica Gill</h3>
    </>
  );
}

[plugin:vite:react-babel] /home/learn/react/react-from-scratch/src/Course.jsx: Unterminated JSX contents. (7:9).

Using the self-closing <br /> will fix this error:

export default function Course() {
  return (
    <>
      <h2>Course: Statistics</h2>
      <br />
      <h3>Instructor: Monica Gill</h3>
    </>
  );
}

Similarly, be careful with tags that you usually don’t have to close in standard HTML - <meta>, <hr>, <br>, <img>, <input> etc. Always remember to close them if you use them in JSX.

JSX Comments

Another JSX quirk is that the regular JavaScript comments using double slash, //, won’t work in JSX. Instead, use {/* */} to wrap comments in the JSX code:

export default function Course() {
  // This is a JavaScript Comment
  return (
    <>
      <h2>Course: Statistics</h2>
      {/* This is a JSX Comment */}
      <h3>Instructor: Monica Gill</h3>
    </>
  );
}

Embedding JavaScript Variables in JSX

So far, our Course component is static - the course title and instructor are hardcoded in the JSX. Let’s change that.

You can embed a JavaScript variable in JSX by wrapping it in curly brackets.

For example, the below code creates two variables and assigns them values. Then, it inserts them in the JSX code.

export default function Course() {
  // define variables & assign them values 
  const title = "Statistics";
  const instructor = "Monica Gill";
 
  return (
    <>
      {/* Embed variables in JSX */}
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
    </>
  );
}

React automatically replaces the embedded variables with their values when it renders the JSX code.

Embedding Component Props in JSX

In the previous section, we defined JavaScript variables within the component and inserted them in JSX.

We can do better. We can make the component truly dynamic by passing props, which can be embedded in JSX like any other variable:

// Component accepts two props
export default function Course({ title, instructor }) {
  return (
    <>
      {/* Embed props in JSX */}
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
    </>
  );
}

Now, you can use this component to render two distinct courses without changing the component code:

  <Course title="Statistics" instructor="Monica Gill" />
  <Course title="Economics" instructor="Rose Jackson" />

Conditional Rendering Using Logical AND

Sometimes, you may need to render certain JSX content only if a specific condition is true. You can use the logical AND operator, &&, to accomplish this:

{Condition && JSX Content}

The condition could be a boolean flag. Or you could check if a particular variable is populated, i.e., it’s not null or empty.

Allow me to illustrate this with an example.

Let’s pass the course description as a prop to our component. If the prop is populated, we want to render the description using a <p> tag. Here’s the code to do this using Logical AND:

export default function Course({
  title,
  instructor,
  description
}) {
  return (
    <>
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
 
      {/* Render description only if it's populated */}
      {description && <p>Description: {description}</p>}
    </>
  );
}

Conditional Rendering Using Ternary Operator

In the last section, you learned how to display or not display content. What if you want to show two different versions of the content based on a specific condition?

You can do that using the ternary operator. Here’s the general syntax:

{Condition ? Content1 : Content2}

If the Condition is true, it’ll render Content1. Otherwise, it’ll display Content2.

Let’s see this action.

We’ll pass a new prop called isOnline to our component. It’s a boolean flag indicating whether the course is available online or not.

The below code changes the rendered text based on the input prop. It uses the ternary operator to display Yes if isOnline is true. Else, it shows No:

export default function Course({
  title,
  instructor,
  description,
  isOnline,
}) {
  return (
    <>
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
      {description && <p>Description: {description}</p>}
      
      {/* Render Yes or No based on boolean flag */}
      <p>Available Online: {isOnline ? "Yes" : "No"}</p>
    </>
  );
}

Render Array Items Using map()

The JavaScript method map() generates a new array by transforming each item of the original array.

You can use map() to create and render JSX elements for each item of an array. I’ll illustrate this with an example.

Let’s say you want to enhance our component by displaying the list of course topics. We’ll pass the topics list as a prop. Then iterate over the topics list using map(), and render each topic in a <p> tag:

export default function Course({
  title,
  instructor,
  description,
  isOnline,
  topicList,
}) {
  return (
    <>
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
      {description && <p>Description: {description}</p>}
      <p>Available Online: {isOnline ? "Yes" : "No"}</p>
 
      {/* Iterate over topics and render them */}
      {topicList.map((topic) => (
        <p>{topic}</p>
      ))}
 
    </>
  );
}

Now we can display a course with the topic list by using the Course component like below:

<Course
  title="Statistics" instructor="Monica Gill"
  description="A beginner-friendly statistics course"
  isOnline={true}
  topicList={[
    "Introduction", "Descriptive Statistics",
    "Probability","Inferential Statistics",
    "Correlation and Regression",
  ]}
/>

Unique Key Error

React will display the course using the code from the above section. But you’ll see the below warning in the browser console:

Warning: Each child in a list should have a unique “key” prop.

When we use the map() method to render array items, we must pass a unique key to each item. React uses this key to identify and efficiently update the item if re-rendering is required.

We can fix the warning by adding the key prop to the <p> tag containing the topic.

The map() method provides the index of the current item as an argument to the callback function. Let’s use that index (idx) as the unique key:

export default function Course({
  title,
  instructor,
  description,
  isOnline,
  topicList,
}) {
  return (
    <>
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
      {description && <p>Description: {description}</p>}
      <p>Available Online: {isOnline ? "Yes" : "No"}</p>
 
      {topicList.map((topic, idx) => (
        <p key={idx}>{topic}</p>
      ))}
 
    </>
  );
}

Show Array as an HTML List

The code to display the course topics from the above section looks good. But we can make it better:

  • We’ll run into an error if topicList is null. Therefore, we should display topics only if topicList is populated. Let’s do that by applying conditional rendering using logical AND.
  • Display the topics as an HTML list.

Here’s the updated code with these improvements:

export default function Course({
  title,
  instructor,
  description,
  isOnline,
  topicList,
}) {
  return (
    <>
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
      {description && <p>Description: {description}</p>}
      <p>Available Online: {isOnline ? "Yes" : "No"}</p>
 
      {/* Render only if topicList is populated. 
          Render topics as an HTML list  */}
      {topicList && (
          <ul>
            {topicList.map((topic, idx) => (
              <li key={idx}>{topic}</li>
            ))}
          </ul>
      )}
 
    </>
  );
}

Using ‘style’ Attribute for Inline CSS Styles

The HTML style attribute works in JSX, but it needs a few tweaks:

  • Use camelCase for style property names in JSX. For example, the regular HTML/CSS property background-color won’t work in JSX. You’ll need to use backgroundColor instead.
  • Pass the style properties as a JavaScript object instead of a semicolon-delimited string used in regular HTML/CSS.

Let’s see a working example.

Suppose you want to style the Course component. You can replace the top-level fragment with a div and set inline styles:

export default function Course({
  title,
  instructor,
  description,
  isOnline,
}) {
  return (
    <div
      style={{
        backgroundColor: "#F7F7F7",
        boxShadow: "rgba(0, 0, 0, 0.24) 0px 3px 8px",
        marginTop: "1rem",
        padding: "1rem",
      }}
    >
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
      {description && <p>Description: {description}</p>}
      <p>Available Online: {isOnline ? "Yes" : "No"}</p>
    </div>
  );
}

Using ‘className’ Attribute to Apply Styles

We use class attribute to specify CSS classes in regular HTML. We cannot use it in JSX because the class is a reserved keyword for defining JavaScript classes.

So what’s the alternative? In JSX, we need to use the className attribute to set CSS classes.2

For example, let’s say we want to show the course details as a styled card. We can set the className attribute to the CSS class card like below3:

export default function Course({
  title,
  instructor,
  description,
  isOnline,
}) {
  return (
    <div className="card">
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
      {description && <p>Description: {description}</p>}
      <p>Available Online: {isOnline ? "Yes" : "No"}</p>
    </div>
  );
}

Passing Event Handlers as JSX Props

JSX lets you define and use custom functions to handle events. You can pass the function as a prop to the desired JSX element. The event props usually start with the word on and are written in camelCase (e.g., onClick, onChange).

Let me show this with an example.

Let’s add a button to enable users to enroll in a course. We’ll handle enrollment in a custom function, enrollmentHandler(). Therefore, we’ll set this function as the value of the onClick prop:

export default function Course({
  title,
  instructor,
  description,
  isOnline,
}) {
 
  function enrollmentHandler() {
    // Logic to enroll goes here 
    alert("You're enrolled.");
  }
 
  return (
    <>
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
      {description && <p>Description: {description}</p>}
      <p>Available Online: {isOnline ? "Yes" : "No"}</p>
 
      {/* call enrollmentHandler() on button click */}
      <button onClick={enrollmentHandler}>Enroll</button>
    </>
  );
}

Passing Arguments to Event Handlers

What if you want to pass some information to an event handler function? Let me show you how to do this using an example.

Suppose you want to customize the function enrollmentHandler() from the previous section. You’d like to pass two arguments:

  • eventType: captures what kind of event triggered the enrollment.
  • courseTitle: which course is the user enrolling in?

Lines 8-11 in the code below show the updated handler with these arguments.

Line 22 shows how to use the updated function. It sets enrollmentHandler() as the onClick handler. Let’s break down what’s happening in that line of code:

  • It uses an arrow function to set the event handler.
  • The arrow function takes the event object (e) as a parameter.
  • The code calls enrollmentHandler() within the arrow function and passes two arguments to it:
    • e.target.value: the value of the element that triggered the event. We’ll use this as the event type.
    • title: the course title passed as a prop to the component.

export default function Course({
  title,
  instructor,
  description,
  isOnline,
}) {
  
  function enrollmentHandler(eventType, courseTitle) {
    console.log(`'${eventType}' event occurred`);
    alert(`You're enrolled in '${courseTitle}' course.`);
  }
 
  return (
    <>
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
      {description && <p>Description: {description}</p>}
      <p>Available Online: {isOnline ? "Yes" : "No"}</p>
 
      <button 
        value="Enrollment" 
        onClick={(e) => enrollmentHandler(e.target.value, title)}>
        Enroll
      </button>
    </>
  );
}

Dynamically Change Styles and Labels

Let’s make our Course component a little adaptive.

We’ll add a new boolean prop, canEnroll, to indicate whether the course is open for registration. We’ll use this prop to alter the behavior of the enrollment button:

  • If canEnroll is true, enable the button and set its test to Enroll.
  • Otherwise, disable the button and change the text to Enrollment Closed.

Let’s review how the code below accomplishes this:

  • Line 20 sets the attribute disabled to the opposite of canEnroll. So the button will be enabled and clickable only if canEnroll is true.
  • Line 21 dynamically sets the CSS classes3 using template literal and conditional rendering. That’ll change the button’s UI as per the canEnroll value.
  • Line 24 sets the button text based on whether the enrollment is open.

export default function Course({
  title,
  instructor,
  description,
  isOnline,
  canEnroll,
}) {
  function enrollmentHandler() {
    alert("Enrollement handler called");
  }
 
  return (
    <div className="card">
      <h2>Course: {title}</h2>
      <h3>Instructor: {instructor}</h3>
      {description && <p>Description: {description}</p>}
      <p>Available Online: {isOnline ? "Yes" : "No"}</p>
 
      <button
        disabled={!canEnroll}
        className={`btn ${canEnroll ? "active" : "disabled"}`}
        onClick={enrollmentHandler}
      >
        {canEnroll ? "Enroll" : "Enrollment Closed"}
      </button>
    </div>
  );
}

Summary

Phew! I’m glad you’ve made it this far. You now have a good understanding of what JSX is and how to use it like an expert.

Here’s a quick recap of what you’ve learned:

  • Why is JSX a better way to build UI than React.createElement().
  • How JSX is more stringent than HTML.
  • Mix HTML tags with JavaScript variables and component props to create dynamic UIs.
  • Render array elements using the JavaScript map() function.
  • Apply CSS using the JSX attributes style and className.
  • Conditional rendering using && and ternary operator.
  • Use custom methods to handle JSX events, such as onClick().

That’s it for today. Happy coding!

Footnotes

  1. Transpilers (such as Babel) will convert JSX into JavaScript code so the browsers can understand it.

  2. Here are a few of the JSX attributes that differ from HTML:
    • JSX: className. HTML: class.
    • JSX: htmlFor. HTML: for. This is used for labels.
    • JSX: tabIndex. HTML: tabindex.

  3. I assume CSS classes such as card, btn, active, disabled exist, and you’ve imported it at the React app or component level. 2