What are elements?

At stated on React's official website, in the Glossary page:

React elements are the building blocks of React applications.

Without any doubt, 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 represent the content of a React app.

As you might know, an HTML DOM tree is comprised of many different kinds of nodes, the most important of which are element and text nodes. React, as we learnt before, maintains its own virtual DOM tree, and this tree is also comprised of React elements.

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 one of the core concepts of React.

Fortunately, once again, React's website precisely defines what exactly an element. Based on that, 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 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, in the previous chapter, we learnt that a component instance is created when we create a React element whose type is a reference to the component (function or class). Hence, React elements are also the way to instantiate concrete component instances, besides instantiating HTML DOM elements.

Moving on, every single element in React is mainly comprised of 2 things: a type and 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.

Crystal-clear.

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

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.

Here's its syntax:

createElement(type, props, ...children)

The first type argument specifies the type of the element (which we've already understood above); the second props provides a set of props; while the third and later arguments all specify child React nodes to be put inside the element being created.

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.

To achieve the following HTML markup using React,

<h1>Hello World!</h1>

we'd write the following code:

React.createElement('h1', null, 'Hello World!')

To create the <div> element below,

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

we'd write the following:

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

Similarly, to create the nested markup below,

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

we'd write the following:

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

If typing React.createElement() seems too long, we could easily simplify it to a shorter identifier, using a named import and then an alias for it:

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

Frankly speaking, React.createElement() isn't really difficult to use. But equally, it isn't even that intuitive.

Developers almost never develop complex React apps purely by typing the code as haphazard sequences of React.createElement() calls. Instead, they always prefer the syntactic sweetness of JSX over this.

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

For instance, the following element,

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

could be represented in JSX as follows:

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

And the same <section> element above could be represented in JSX as follows:

<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?

Now, let's see what we get in return after calling createElement().

Besides creating elements for DOM elements, we can also create elements to instantiate given components. This mandates that the first arg to createElement() be a reference to the component (function or class).

In the code below, we define a Greeting component and then render an instance out of it by creating a React element for it:

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

const element = React.createElement(Greeting, null);

Live Example

Working with components has so many aspects to it that we've denoted a separate chapter, React Components, to explore them in detail.

Inspecting React elements

React.createElement() returns back a pure JavaScript object (i.e. inheriting only from the Object class) 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 code:

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

console.log(element);

The console displays the following for this element:

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

As you can see here, there are many properties of element, including props and type as we learnt above.

type is 'h1' because the element is a DOM element abstracting the <h1> element. props contains the properties className, as we defined it on the JSX element, in addition to another prop called children. We'll discover children later on in this chapter.

The rest of the two public properties — key and ref — will be explored in later chapters.

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

Immutability of elements

React elements feature an extremely important idea that sits right at 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:

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

element.props.x = 10;
Uncaught TypeError: Cannot add property x, object is not extensible ...

Live Example

To change an element, we ought to create a new element.

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 raised clear saying 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 in React's production build, we won't 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. No. We must always treat elements in React as immutable, no matter which build we are in.

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, done with the help of props. We can't access the state of given elements (component instances to be specific) by inspecting them — the state data is stored and handled internally by React itself.

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 that work just as we are used to seeing.

Now you might be thinking why is this so? Let's see why.

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, prevents the chances of errors in the program, and thus makes development easier.

As we saw above, React elements don't have any methods available on them (obviously, apart from the methods of 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 find apps storing React elements in identifiers and then working with those identifiers.

The de facto approach in apps is to directly pass the main React element, which is an instance of a component, to render() (as we need to do so only once and for all) and not have to create a constant such as element to hold it and then pass on this constant to render().

We've been using the non–de facto approach uptil now in this course because we simply haven't yet explored components to the core. As soon as we do, we'll switch gears and use the de facto approach of structuring React code.

The children prop

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

But before that, let's take an example.

Consider the following JSX code:

<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.

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

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

Clearly, not anything difficult to understand.

Now this same code could be expressed as follows, leveraging the children prop.

This is shown below:

React.createElement('section', {
   children: [
      React.createElement('h1', null, 'A heading'),
      React.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 a <section> React element.

When we run the code, we get the exact same output as before.

Live Example

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

<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. This expression turns out to be an array holding two JSX elements.

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,

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

the createElement() method itself groups all of the arguments, starting from the third one and then onwards from there, inside an array and then assigns this array to the children prop of the element.

This can be confirmed by manually inspecting the children prop of this element:

{
   $$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
}

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 one is <p>.

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

When rendering, React uses the children prop of a given element to determine the children that it needs to take into account alongside that element, and then the children prop of each of those children themselves, and so on, until it reaches the very bottom of the tree of nodes originating from the main element.

To boil it down, whenever we nest elements inside other elements in React, we, in effect, implicitly consume the children prop.

But children isn't meant to always be an implicit feature — sometimes we can use it as well. And we shall indeed do so once we discover components in detail in the upcoming chapter React Components.