Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 2

JSX and React.createElement Under the Hood

Recommended resources

In our previous lesson, we successfully established a modern React development environment using Vite. You now have a running project and a foundational understanding of its structure. Today, we'll shift our focus from the setup to the syntax at the heart of React development: JSX.

By the end of this lesson, you will be able to explain the role of JSX and, crucially, how this HTML-like syntax is transformed into plain JavaScript function calls that the browser can execute. Understanding this "magic" is fundamental to thinking in React and will set the stage for building your first components.

What is JSX?

When you open the src/App.jsx file in the project we created, you'll see something that looks like HTML inside a JavaScript function. This is JSX.

JSX stands for JavaScript XML. It's a syntax extension for JavaScript that allows you to write markup directly within your code. Instead of manually creating DOM elements with methods like document.createElement() and appending them, JSX provides a more declarative and readable way to describe what your user interface should look like.

However, it's critical to remember that browsers do not understand JSX natively. It is not valid JavaScript. It's what we call "syntactic sugar"—a syntax designed to make things easier for humans to read and write, which is then converted into code that a computer can run.

The following resource provides a concise explanation of what JSX is.

How JSX (React) Works Under the Hood

This article from Telerik gives a clear definition of JSX and its purpose in the React ecosystem.

Please read the introduction and the section What Is JSX?. This will solidify its definition as a syntax extension that looks like HTML but is fundamentally JavaScript.


From JSX to JavaScript: The Transpilation Process

So, if browsers can't read JSX, how does it work? The answer is transpilation. During the build process (which Vite handles for us), a tool called a transpiler—in our case, Babel—scans the code, finds all the JSX, and converts it into standard JavaScript.

Specifically, each JSX element is transformed into a function call: React.createElement(). This is the core concept you need to grasp. The HTML-like syntax is simply a convenient shorthand for a series of nested function calls.

The following video provides an excellent visual demonstration of this transformation.

What is JSX?

In this video, "What is JSX?", Harry Wolff uses the Babel REPL (Read-Eval-Print Loop) to show exactly how JSX code is converted into React.createElement() calls in real-time.

Watch the segment from compile time transformation. Pay close attention to how changing the JSX on the left immediately changes the resulting JavaScript function call on the right. This visualization makes the concept of transpilation very concrete.

Now that you've seen the transformation in action, let's look at some code examples that break it down.

How JSX (React) Works Under the Hood

This section of the Telerik article provides side-by-side code examples, showing how both simple and nested JSX structures are compiled into React.createElement() calls.

Read the sections JSX With React in Scope. Focus on the two main examples: The simple <h1> tag and its compiled React.createElement("h1", {}, "Hello, World!") equivalent. The more complex div with a nested list, which demonstrates how nesting JSX creates nested React.createElement() calls.

As you can see, writing complex, nested UI structures with React.createElement() directly would be verbose and difficult to maintain. Your experience with HTML's clear, hierarchical structure makes the appeal of JSX immediately obvious—it provides the readability of markup with the power of JavaScript.


Dissecting React.createElement()

To fully understand the mapping from JSX to JavaScript, we need to examine the function that JSX compiles to. The official React documentation is the best source for this.

React.createElement(type, props, ...children)

Let's break down its arguments:

  • type: This specifies the element to be created. It can be a string for a standard HTML tag (e.g., 'div', 'p') or a reference to a custom React component (which we'll cover soon).
  • props: This is a JavaScript object containing all the attributes you pass to the element. For example, <h1 className="title"> becomes { className: 'title' }.
  • ...children: Everything between the opening and closing tags. This can be a string of text or, for nested elements, more React.createElement() calls.

The function returns a lightweight JavaScript object known as a React Element. This is not a DOM element; it's a description of what should be rendered to the DOM. React uses these objects to construct the actual DOM and keep it in sync with your application's state.

createElement

The official React documentation for createElement provides the definitive guide to its usage and arguments.

Start by reading the initial summary and the reference section for the function signature. Then, in the "Usage" section, review the code examples under Creating an element without JSX. These side-by-side comparisons of createElement and its JSX equivalent are particularly helpful.


An Important Update: The New JSX Transform

In older React projects, you would see import React from 'react' at the top of every file that used JSX. This was necessary because when JSX was compiled to React.createElement(), the React variable had to be in scope.

With modern versions of React (including the one in your Vite project), this is no longer required. The JSX transform was updated to automatically import the necessary functions from a special package (react/jsx-runtime) behind the scenes.

So, while the fundamental principle of compiling to a function call remains the same, the mechanics are more streamlined. You can now write JSX without the mandatory React import.

The Telerik article you read earlier has a great section explaining this update.

How JSX (React) Works Under the Hood

This part of the article explains the modern JSX transform introduced in React 17.

Read the section JSX Without React in Scope. Notice how the compiled output changes from React.createElement() to _jsx() and how the compiler handles the import for you.


Essential JSX Rules

As you begin writing JSX, there are a few important rules to keep in mind, most of which stem from the fact that JSX is JavaScript, not HTML.

  1. Return a Single Root Element: A component can only return one top-level element. If you need to return multiple elements, you must wrap them in a single parent, like a <div> or a React Fragment (<>...</>).
  2. Close All Tags: In JSX, every tag must be closed. For tags that don't have content, like <input> or <img>, you must use a self-closing tag: <input />, <img />.
  3. Use className for CSS Classes: Because class is a reserved keyword in JavaScript, you must use the className attribute to assign CSS classes.
  4. Component Names Must Be Capitalized: Standard HTML elements are written in lowercase (e.g., div). When we create our own custom components, their names must start with a capital letter (e.g., <MyButton />). This convention allows the JSX transpiler to distinguish between HTML tags and React components.
  5. Use Curly Braces {} for JavaScript: To embed a JavaScript expression inside JSX, you wrap it in curly braces. For example: <h1>{user.name}</h1> or <div style={{ color: 'blue' }}>.

These rules will quickly become second nature as you start building components.


Conclusion

In this lesson, we've pulled back the curtain on JSX, one of React's most powerful features. You've learned that it's more than just "HTML in JS"; it's a carefully designed syntax that makes UI development declarative and readable.

Here are the key takeaways:

  • JSX (JavaScript XML) is a syntax extension that provides an HTML-like way to describe UI components.
  • It is not understood by browsers directly; it is transpiled into React.createElement() function calls by tools like Babel.
  • The React.createElement() function takes a type, a props object, and children as arguments, and returns a lightweight React Element object.
  • Modern React versions use a new JSX transform that removes the need to manually import React into every component file.
  • Writing JSX involves following a few key rules, such as returning a single root element and using className instead of class.

Now that you understand the syntax we'll be using, our next lesson will put it into practice. We will learn how to create our own functional components—the fundamental building blocks of any React application—which are essentially JavaScript functions that return JSX.