React: Foundation — Elements

React Elements

Learning outcomes:

  • What are React elements
  • Creating React elements using createElement()
  • Inspecting React elements — the type and props properties
  • Immutability of elements
  • The children prop

What are elements?

At stated on React's (old) glossary page:

React elements are the building blocks of React applications.

Indeed, elements represent one of the most fundamental principles of React. A React app is nothing without elements.

React elements work pretty much the same way as HTML elements. They define certain pieces of content to have a special meaning to them, for e.g. a piece of content that denotes a 'chat box', a piece of content that denotes a 'slider', etc.

It's good to know that React elements are the building blocks of React apps. But still, this ain't a precise technical definition of what exactly is a React element.

Fortunately, once again, React's (old) website helps us in this regard. We can define a React element as follows:

A React element is an abstraction over a DOM element or a component instance.

This definition ain't that difficult to comprehend.

It says that a React element either represents an HTML DOM element (which is a concept of the HTML DOM) or a component instance (which is a concept of React).

For example, we all now know that the <h1> React element is simply an abstraction over the DOM <h1> element. So is the case with <div>. So is the case with <span>. So is the case with all HTML elements.

As for component instances, we learnt in the previous React Basics chapter that a component is a function returning a React node whereas a component instance is an actual element, instantiating the component.

For example, the <Greeting> element represents a component instance of the component Greeting (remember, Greeting is a function). So is the case with the element <ChatBox>, instantiating the component ChatBox.

Besides this, every single element in React is mainly comprised of 2 things:

  • A type
  • A set of props

The type specifies whether the element represents a DOM element or a component instance. If the type is a string, e.g 'h1', the element represents a DOM element. Otherwise, asserting that the type is a function or a class, the element represents a component instance.

Props works similar to attributes of HTML elements and properties of DOM elements. That is, they hold additional data for the element, essentially serving to describe its content, styles, and interactions.

Let's now review how to create elements in React.

Creating elements in React

A React element is created via the createElement() method exported by the react library, as we saw in the previous chapters.

Here's its syntax:

createElement(type, props, ...children)
  • The first type argument specifies the type of the element (which we've already discussed above ).
  • The second props arguments provides a set of props.
  • From the third argument onwards, every single argument specifies a child React node to be put inside the element being created. Any of these arguments can also be an array of React nodes (more on that later).

As is obvious, since React elements are also valid React nodes, the third and later arguments can be elements themselves.

Let's consider a handful of examples.

React elements representing HTML DOM elements

We'll start off with seeing how to create React elements representing HTML DOM elements.

To get the following HTML element using React,

HTML
<h1>Hello World!</h1>

we'd write the following code:

JavaScript
createElement('h1', null, 'Hello World!')

To create the <div> element below,

HTML
<div class="intro-sect" id="d1">The intro section<div>

we'd write the following:

JavaScript
createElement(
   'div',
   { className: 'intro-sect', id: 'd1' },
   'The intro section'
);

Similarly, to create the nested markup below,

HTML
<section>
   <h1>A heading</h1>
   <p>A paragraph</p>
   <div>A div with a <span>span</span>.</div>
</section>

we'd write the following:

JavaScript
createElement('section', null,
   createElement('h1', null, 'A heading'),
   createElement('p', null, 'A paragraph'),
   createElement('div', null,
      'A div with a ',
      createElement('span', null, 'span'),
      '.'
   ),
);

If typing createElement() seems too long, we could easily simplify it to a shorter identifier using a name alias when importing it, as shown below:

JavaScript
import { createElement as e } from 'react';

e('section', null,
   e('h1', null, 'A heading'),
   e('p', null, 'A paragraph'),
   e('div', null,
      'A div with a ',
      e('span', null, 'span'),
      '.'
   ),
);

Live Example

Nonetheless, while createElement() isn't any difficult to use, it certainly isn't that elegant.

Developers almost never develop complex React apps directly by creating the UI as sequences of createElement() calls. Instead, they always prefer the syntactic sweetness of JSX over this.

Remember JSX from the previous chapters?

JSX elements are purely simplifications over manually invoking createElement(). They resemble HTML/XML elements, which all web developers are well-accustomed to writing, and that's what makes JSX so easy and intuitive to work with.

For instance, the following element,

HTML
<h1>Hello World!</h1>

could be represented in JSX as follows:

JSX
<h1>Hello World!</h1>

On the same lines, the following <div>:

HTML
<div class="intro-sect" id="d1">The intro section<div>

could be represented in JSX as follows:

JSX
<div className="intro-sect" id="d1">The intro section<div>

And the same <section> example above, containing a set of elements nested inside of it, could be represented in JSX as follows:

JSX
<section>
   <h1>A heading</h1>
   <p>A paragraph</p>
   <div>A div with a <span>span</span>.</div>
</section>

Live Example

Absolutely amazing, isn't this?

React elements representing component instances

Now, let's see how to create React elements representing instances of given components.

In the code below, we define a Greeting component that returns a very basic greeting text:

JavaScript
function Greeting() {
   return 'Hello World!'
}

To obtain an instance of this component, we'll again call createElement(), albeit this time its first argument will be the component function, not any kind of a string:

JavaScript
createElement(Greeting, null);

Using JSX, we can simplify this as follows:

JSX
<Greeting></Greeting>

In fact, as we learnt in React Basics and as we'll learn again in the upcoming chapters, since <Greeting> doesn't have any children here, it can be further simplied to just one tag:

JSX
<Greeting/>

When we have just one tag, it's important for it to have the / at its end. Without the /, the JSX parser will throw an error:

JSX
// Tag doesn't have an ending '/'
<Greeting>

Now that we have a good understanding of how to create React elements using createElement() and JSX (which merely converts back to createElement()), it's time to look into what exactly is returned by a createElement() call.

Inspecting React elements

createElement() returns back a pure JavaScript object (i.e. inheriting from the Object interface) with a handful of properties.

The ones that are particularly important are:

  • type — the type of the element.
  • props — an object holding the props of the element.

Consider the following JSX code:

JSX
const element = (
   <h1 className="heading">Hello World!</h1>
);

console.log(element);

which is equivalent to the following createElement() code:

JavaScript
const element = createElement(
   'h1',
   { className: 'heading' },
   'Hello World!'
);

console.log(element);

Here's what the console displays:

{
   $$typeof:  Symbol(react.element)
   key:  null
   props:  {className: 'heading', children: 'Hello World!'}
   ref: null
   type: 'h1'
   _owner:  null
}

Alright, so there are a handful of properties to digest here. Let's go one-by-one over them.

  • type is 'h1', a string, because the React element represents an <h1> DOM element.
  • props contains all the props provided to the element at the time of creation.
  • ref contains an object holding the refs of the element. We'll learn more about refs in the React Refs chapter.
  • key defines a unique key for the element, used to signal to the reconciler when an element has changed or hasn't changed. We'll learn more about keys in React Keys.

The properties $$typeof and _owner are meant for internal use by React and so we won't be going over them here.

Immutability of elements

React elements feature an extremely important idea that aligns with the very design ideology of React — immutability.

In particular, React elements are immutable in nature. This means that once a React element is created, it can NOT be mutated, i.e. it can't be modified.

Here's an illustration:

JSX
const element = (
   <h1>Exploring immutability</h1>
);

element.props.x = 10;

We're trying to add a prop x manually to the element via the props object on element. However, since all elements are immutable in React, we get an error in doing so:

Uncaught TypeError: Cannot add property x, object is not extensible ...

Live Example

Even if the prop x already existed on the element, we still couldn't have modified its value using element.props.x.

If we wish to change the data (the props) of an element in React for some reason, we ought to create a new element with the new set of data. An existing element can't be mutated in any way. As simple as that.

As you may know, this isn't how DOM elements work. In the DOM, we can mutate element nodes; in fact, this is the very design of the DOM — to be able to mutate elements.

In effect, React elements are merely descriptions of how actual DOM elements should look on the webpage, supplemented with the idea of props. There's little to almost no sense in going on and changing this description of an element in React.

It's all upto React and the rendering engine being used (React DOM in our case) to be able to understand these descriptions and turn them into interactive trees of DOM nodes.

Why are elements in React immutable?

Well, the answer to this is pretty much the same as for the answer to why strings are immutable in JavaScript.

Immutability prevents mutation and, likewise, prevents side effects from happening in a program. This, in turn, reduces the chances of errors in the program and makes development and testing much easier.

As we saw above, React elements don't have any methods available on them (obviously apart from the methods inherited from the Object interface) and so, in this way, they are of little use to us if we want to store them.

You won't almost ever — in fact, never — find real-world apps storing React elements in identifiers and then working with those identifiers.

The de facto approach in React is to directly pass a React element to render() or to createElement()NOT store the element in any identifier.

To be more specific, we don't need to store the object returned by the JSX <h1> element in element in the following code:

JSX
import React from 'react';
import ReactDOM from 'react-dom/client';

// To need to store the element in `element`
const element = (
   <h1>Hello World! (A non-standard approach)</h1>
);

const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(element);

As a matter of fact, but not related to the idea of elements, we don't even need the constant root, for we can directly call render() on ReactDOM.createRoot().

Here's how the code above can be simplified, letting go off the constants:

JSX
import React from 'react';
import ReactDOM from 'react-dom/client';

ReactDOM.createRoot(document.querySelector('#root')).render(
   <h1>Hello World! (A non-standard approach)</h1>
);

Do note that even though this works absolutely fine, still we haven't touched upon the very convention that React encourages us to stick to render an app — to instantiate an App component and provide to the component instance to render().

We'll consider this convention in detail in the chapter, React Components.

Immutability only in React's development build

It's only when working with the development build of React that elements are immutable. The code above was executed in the development build and, as a consequence, we got an error thrown, reading that it ain't possible to mutate the React element.

In the production build, however, React elements are mutable. Hence, if we run the same code above, the one assigning to element.props.x, in React's production build, we would NOT run into any errors.

But this doesn't mean that we should mutate elements and stick to using the production build to be able to do so. A big no! We must always treat elements in React as immutable, no matter which build we're currently in.

The reason of making React elements mutable in the production build is to result in a better performance. In production, React can assert that all its guidelines are being abided by and, likewise, doesn't need to put strict measures in place, unlike during development.

The children prop

While we're learning about elements in React, it is germane to talk about the children prop and what purpose it serves.

But before that, let's take a look at an example.

Consider the following JSX code:

HTML
<section>
   <h1>A heading</h1>
   <p>A paragraph</p>
</section>

Notice the children of <section> here carefully. They are <h1> and <p>, in this very order, right?

If we come up with the corresponding createElement() code for this JSX, it'll be something along the following lines:

JavaScript
createElement('section', null,
   createElement('h1', null, 'A heading'),
   createElement('p', null, 'A paragraph')
);

Clearly, not anything difficult to understand. Now, let's slightly modify it.

This same code could be expressed as follows, leveraging the children prop (don't worry, we'll be explaining children in depth very shortly below):

JavaScript
createElement('section', {
   children: [
      createElement('h1', null, 'A heading'),
      createElement('p', null, 'A paragraph')
   ]
});

The children prop is set to an array holding the same two React elements, in the exact same order, that we provided in the code above while creating the <section>.

Running this code produces the exact same output as before.

Live Example

The corresponding JSX could obviously leverage children as well, as shown below:

JSX
<section children={[
   <h1>A heading</h1>,
   <p>A paragraph</p>
]}></section>

The children attribute is followed by a pair of curly braces ({}) to indicate that a JavaScript expression follows, which in this case is an array holding two JSX elements, <h1> and <p>.

Of course, it isn't desirable to use children in this way in the JSX, but at least the idea of how children can be used is made clear.

The question is: What exactly is children?

Well, as the name suggests:

The children prop of a React element holds a collection of all its children.

When we do something like the following,

JavaScript
createElement('section', null,
   createElement('h1', null, 'A heading'),
   createElement('p', null, 'A paragraph')
);

the createElement() function itself collects all of the arguments starting from the third one inside an array, and then assigns this array to the children prop of the element created.

This can be confirmed by manually inspecting the children prop of the <section> element created above:

{
   $$typeof:  Symbol(react.element)
   key:  null
   props:  {
      children: [
         { $$typeof:  Symbol(react.element), type: 'h1', key:  null, ... }
         { $$typeof:  Symbol(react.element), type: 'p', key:  null, ... }
      ]
   }
   ref: null
   type: 'section'
   _owner:  null
}

As can be seen, the props property of the returned element has a children prop pointing to an array containing two React elements, the first of which is <h1> and the second is <p>.

In other words, what we were manually doing by assigning a value to the children prop when invoking createElement() in the code snippets above, it is automatically done by createElement() behind the scenes.

So what's the significance of children?

Well, when any given React element is rendered, React uses the children prop to traverse down the entire tree originating from that element and then rendering all of it.

In other words, children is key to React itself in order to determine what is contained within an element so that it could go over all of those contained elements, and then repeat this process for those elements themselves, until the entire app/root is rendered.

To boil it down, whenever we nest elements inside other elements in React, we are in effect consuming the children prop without us knowing of it.

But children isn't always meant to be an implicit feature; sometimes we can explicitly use it as well. As a matter of fact, it's quite common for real-world React apps to use children every now and then.

And we shall indeed do so as well once we discover components in detail in the upcoming chapter, React Components.

Spread the word

Think that the content was awesome? Share it with your friends!

Join the community

Can't understand something related to the content? Get help from the community.

Open Discord

 Go to home Explore more courses