React nodes
At this stage, we have already worked with React elements for a while, from the previous chapter where we used React.createElement()
and then some syntactic JSX sugar over it to create them. Let's now dig deeper into what React elements are, and even before that, what React nodes are.
At the core, all React applications are made up of components (which we shall explore later), which are themselves composed of React nodes. Think of React nodes as nodes of an HTML DOM tree.
The official documentation of React doesn't formally define or use the phrase 'React nodes.' We use it because of two reasons:
- Type declarations in the React library label the third parameter of
React.createElement()
to be a rest parameter, each of whose arguments implements theReactNode
type. (If you've worked with TypeScript before, you'll be able to understand what this means.) - Using the term 'React node' simplifies the discussion when we want to talk about
React.createElement()
and/or some content to be rendered by React.
Akin to HTML DOM nodes, React nodes can either be strings, elements or fragments (more on these later).
But React nodes can also be numbers, Booleans, undefined
and null
.
How exactly each of these values is handled by the renderer will be explored later on as this course proceeds. For now, we'll be concerned with the most important kind of React nodes — elements.
React elements
React elements are described as follows by React's documentation, specifically in Glossary of React Terms:
There's absolutely no doubt in this.
When we build UIs, we always require HTML elements, such as <div>
s, <section>
s, <header>
, <footer>
, <p>
s, <h1>
, <img>
s, and so on and so forth. We just can't build literally anything without these core pieces.
In vanilla JavaScript, we create these HTML elements by constructing HTML DOM elements.
React, however, models these elements as React elements. So in that way:
When we are working in React, the <h1>
element, for instance, is not the same as an <h1>
HTML DOM element. Obviously, the React <h1>
does somewhere reference an HTML DOM <h1>
, but it also includes a host of other utilities, which drive the ideology of React.
React elements are created using React.createElement()
.
As shown before, the first argument is a string representing the tag name of the element to create, the second argument is an object specifying the DOM attributes to set on the element, and from the third argument onwards, every argument specifies a node to put inside the element.
React.createElement()
is also used to create components, when the first argument is a reference to a function. We'll see details to this later on in this chapter.In the previous chapter, we created an <h1>
element with the contenteditable
attribute set and the text 'Hello World!' inside it using the following statement:
const element = React.createElement(
'h1',
{ contentEditable: true },
'Hello World!'
);
Let's now consider a more complex example.
In the code below, we create a <div>
element with an <h1>
and a <p>
inside it (in this very order):
const element = React.createElement(
'div',
null,
React.createElement('h1', null, 'A heading'),
React.createElement('p', null, 'A paragraph')
);
- The main element is
<div>
, likewise the first arg is'div'
. - No attributes are required on the element, likewise the second arg is
null
. - The next two args are React elements themselves, i.e.
<h1>
followed by<p>
, and will be placed inside the<div>
element being created.
Simple.
Although, React.createElement()
isn't imperative like the document.createElement()
DOM method, it still is a bit tedious to type.
You would almost never find big apps, if not even small apps, being made just by using such React.createElement()
calls. Instead, you'll almost always find React elements created using JSX.
A JSX element denotes a React element and resembles the syntax of constructing elements in HTML. That is, we use the <
and >
angle brackets to denote an element's starting and ending tags and then put its content inside these tags.
For instance, the <div>
element shown above could be represented in JSX as follows:
const element = <div>
<h1>A heading</h1>
<p>A paragraph</p>
</div>;
As you can see, the code is just like how we'd express the element in pure HTML.
But, as you might've noticed, because the code is embedded inside JavaScript, there's a good chance that we can mistakenly interpret as part of the actual code in the script. Yes, JSX is written inside a JavaScript file, but at the end of the day, it's JSX and is different from pure JavaScript code.
Writing JSX, as it is, between given JavaScript code is bound to reduce its readability. A better approach is to use parentheses (()
) to hold the JSX code.
JSX simply gets converted to React.createElement()
calls, which are mere JavaScript expressions; likewise, a JSX element itself is just an expression.
The same above could, henceforth, be expressed as follows:
const element = (
<div>
<h1>A heading</h1>
<p>A paragraph</p>
</div>
);
The addition of the parentheses (()
) and the extra indentation of the <div>
element makes the JSX code here much more readable than the one we saw before.
What do you say?
Moving on, attributes of JSX elements work the same way as attributes do in HTML. More precisely, they are simply the properties of the object passed into the second parameter of React.createElement()
, also referred to as props of the element.
The values of JSX attributes can be strings, numbers, Booleans, undefined
, null
, arrays, objects, functions, regular expressions, dates — just about anything!
This isn't counter-intuitive. Remember that JSX elements are converted to React.createElement()
calls, whereby the attributes of those elements are converted to properties of the second argument of the method, and then the values of these properties are simply obtained from the values of the corresponding attributes of the JSX element.
However, there's a specific syntax to use an arbitrary value as the value of a JSX attribute.
If, for instance, we know that the value is a string, it could be easily provided as follows,
<div title="50">A div</div>
just as we're accustomed to do in HTML source code, i.e. encapsulate the value inside a pair of quotes (usually ""
).
But, if the value is NOT a string, and if we specify it as it is, as shown below,
<div title=50>A div</div>
it would automatically get converted to a string by the JSX transformer.
So, the following expression
<div title=50>A div</div>
is equivalent to this:
<div title="50">A div</div>
To correctly set an attribute to an arbitrary value, we need to use curly braces ({}
) to tell the JSX parser that literal code is given inside the braces.
The attribute above, with the numeric value 50
, could likewise be set as follows:
<div title={50}>A div</div>
The value {50}
means that 50
is a JavaScript expression, which we all know is just a simple number.
What are props?
Consider the following JSX element,
<h1 contentEditable="true">Hello World!</h1>
and notice the contentEditable
attribute.
If we see this same element as a React.createElement()
call, the attribute is merely a property of the object passed as the second argument while calling the method:
React.createElement('h1', { contentEditable: "true" }, 'Hello World!')
contentEditable
in both these snippets is a prop.
So what exactly is a prop?
As you can probably guess, prop is a fancy word for 'properties' in React. But a prop isn't just any property.
React.createElement()
.This shorthand word 'prop' really helps us distinguish other properties from the ones that we specifically provide in React.createElement()
invocations.
In a React.createElement()
call, all the properties of the second argument object (except for key
) are the element's props. Pretty simple.
Similarly, in JSX, all the attributes of a JSX element are the props of the corresponding React element (again except for key
).
The purpose of props of React elements is the exact same as the purpose of properties of HTML DOM elements — to customize the element's content, appearance, and/or provide additional data to it.
The nomenclature of many props is the same as the nomenclature of properties in the DOM. This makes sense because React processes these props as properties of DOM element nodes, at some point. If it innovated a lot in this area, like coming up with different property names, it would've had to do a sufficient amount of processing.
Let's consider a very common attribute applied to HTML elements — class
.
Do you know the name of the corresponding DOM property representing the class
HTML attribute of element nodes? Well, it's called className
(to learn why it isn't called class
; refer to JavaScript HTML DOM — Attributes — className
).
The corresponding prop in React is called...you guessed it... className
.
In the code below, we showcase an example of className
on <h1>
:
const element = (
<h1 className="blue">React is amazing!</h1>
);
Supposing that the following CSS is set in the HTML file where the React program is linked,
.blue {
color: blue
}
as soon we launch the page in the browser, we get the following output:
Not that difficult.
className
is an extremely commonly used prop in React apps, just like class
is in HTML documents.
Besides className
, another commonly used prop is style
.
Do you remember the style
HTML DOM property? Do you remember how it works?
In HTML DOM, the style
property of element nodes (in particular, of HTMLElement
instances) points to a CSSStyleDeclaration
object that we could use to manually set any inline CSS style on a given element.
In React, the style
prop works, more or less, the same way.
But since style
is a prop, i.e. a property of the object passed as the second argument to createElement()
, we sensibly can't use it as we'd use the style
property of element nodes. What we have to do instead is to tell React all the inline styles that we want, in one go.
React knows this and thus expects the value of the style
prop to be an object. Each property of this object is internally applied by React to the style
DOM property of the element node.
For example, to set the marginLeft
and marginTop
properties in HTML DOM, we'd do the following, supposing that h1Element
is an <h1>
element node:
// Suppose h1Element is an <h1> element node.
h1Element.style.marginLeft = '70px';
h1Element.style.marginTop = '30px';
But in React, we'd do the following:
const element = (
<h1 style={{ marginLeft: '70px', marginTop: '30px' }}>Hello World!</h1>
);
The value of the style
JSX attribute is a JavaScript literal object with two properties: marginLeft
defining the margin-left
CSS property and marginTop
defining the margin-top
CSS property.
React innovates a little bit in style
's property values.
That is, the value of style properties don't just have to be strings, such as '10px'
. They could be numbers as well (but obviously where using numbers makes sense), such as 10
, in which case they're automatically converted to a '<number>px'
representation internally, where <number>
is the number used.
For example, the value 50
would become '50px'
; the value 20.55
would become '20.55px'
; and so on and so forth.
This can save us from having to do this work manually.
For example, in the code below, we set the <h1>
element's width to 100px
by providing the width
style property the string value '100px'
:
const element = (
<h1 style={{ backgroundColor: 'yellow', width: '100px' }}>Hello World!</h1>
);
But thanks to React, we can simplify this as follows:
const element = (
<h1 style={{ backgroundColor: 'yellow', width: 100 }}>Hello World!</h1>
);
The value of width
is the number 100
, not '100px'
, which is automatically converted to the string '100px'
(not to the string '100'
) by React.
The output remains the same:
Simple amazing.
We'll cover props in detail in the chapter React Props.
Events in React
Let's now talk about events in React, starting with a rudimentary question.
What is the property used to register a click
handler on an element node called? Well, it's simply called onclick
.
Now you might be tempted to think that React abides by this exact same naming, but that's NOT the case. Yes, React does call it the same as onclick
, but with a different casing. In React, all event handler props, unlike event handler properties in the DOM and unlike attributes in HTML, follow the camel casing convention.
So onclick
in React would be called onClick
.
That why exactly is this casing used will be explored in the chapter React Events where we'll learn more about how events work in React and why they use W3C's SyntheticEvent
API instead of the native events.
Anyways, now that we know that onClick
represents the prop used to set up a click
event handler on a React element, let's use it.
In the code below, we demonstrate onClick
using React.createElement()
:
const element = React.createElement(
'h1',
{ onClick: function(e) { alert('Clicked'); } },
'Hello World!'
);
As can be seen, the onClick
prop is assigned a function, just like we'd do to the onclick
property of a DOM element node. The function simply makes an alert.
Here's the output produced:
Quite simple.
Let's now make this a bit more involved by changing the content of the <h1>
element upon the click.
Inside the handler function, we'll call the render()
method of the root
object to change the content of #root
:
import React from 'react';
import ReactDOM from 'react-dom/client';
const element = React.createElement(
'h1',
{ onClick: () => {
root.render(React.createElement('h1', null, 'New content'));
} },
'Hello World!'
);
const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(element);
As soon as we click the <h1>
element, its content indeed changes. This is React in action.
The same code could be expressed in JSX as follows:
import React from 'react';
import ReactDOM from 'react-dom/client';
const element = (
<h1 onClick={() => { root.render(<h1>New content</h1>); }}>Hello World</h1>
);
const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(element);
The onClick
attribute of the <h1>
element is assigned a function value with the help of a pair of curly braces ({}
) to instruct the JSX parser that the attribute's value is a JavaScript expression. This function calls root.render()
, passing it a new <h1>
element.
It works flawlessly.
However, as you might've felt, this code isn't very elegant, not even after the addition of JSX. We are directly triggering the rendering of the React <h1>
element to the #root
element upon its click.
As we keep on adding more and more bells and whistles to the program, the code would just keep on becoming more and more complex and tedious.
For example, let's say we want to implement a counter inside an <h1>
that increments on each subsequent click.
Consider the following code where we illustrate this idea:
import React from 'react';
import ReactDOM from 'react-dom/client';
let counter = 0;
function clickHandler() {
root.render(<h1 onClick={clickHandler}>Count: {++counter}</h1>);
}
const element = (
<h1 onClick={clickHandler}>Count: {counter}</h1>
);
const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(element);
As we click on the element, a new <h1>
React element is rendered inside #root
with its onClick
prop set to the clickHandler()
function.
The value {++counter}
inside the <h1>
element is, yet again, another instance of instructing the parser that a JavaScript expression follows. That is, Count: {++counter}
doesn't render the text 'Counter {++counter}'; instead it renders the text 'Counter: ' followed by the value of the variable counter
after performing the prefix increment operation.
Even though the code does its job, this is NOT how React is designed to be run.
Someone could argue that the code could be simplified further to make it look more elegant, as shown below,
import React from 'react';
import ReactDOM from 'react-dom/client';
let counter = 0;
function incrementCounterAndRender() {
root.render(<h1 onClick={incrementCounterAndRender}>Count: {++counter}</h1>);
}
const root = ReactDOM.createRoot(document.querySelector('#root'));
incrementCounterAndRender();
but still React was NOT made for us to worry about manually handling rendering concerns, as we're doing in all these code snippets.
React could instead do this all on its own, given that we know how to properly use it. And for that, we ought to know how to work with state.
But before that, we need to know about one of the foundational concepts of React — components.
Components — the cornerstone of React
Components are the cornerstone of React. From day one, React has been based on the idea of reusable components to build the UI.
Now what are components?
Well, there are two ways to define a component, one is completely from the perspective of the UI while the other is more technical and tied to the design of React.
Starting with the former:
Let's consider a webpage. What things could a webpage be composed of? It could have a header, a footer, a section where all the main content goes, maybe even some sidebars, and so on.
As another instance, consider the header. What things could the header be composed of on a webpage? Well, it could have a logo, a navbar, even a search bar, and so on.
Components can be composed of components, as the previous example demonstrates, which can be composed of components themselves, and so on.
Now, let's define a component technically.
In modern-day React,
Such components are more precisely referred to as function components, simply because they are based on functions.
In slightly older-day React, which still exists and is based on the idea of classes,
render()
method that returns a React node.As you can probably guess, such components are more precisely referred to as class components, simply because they are based on classes (ES6).
If we want to represent this header in React, we can easily do so using nested JSX, just like nested HTML, as shown below:
<header>
<div className="logo">...</div>
<nav>...</nav>
</header>
This works but it's NOT reusable.
The whole idea of a header isn't encapsulated. For example, if we want to, we can't tie some state data to the header if it's defined in this rudimentary style (we'll see what this means later on).
The ultimate solution is to use a React component. Let's see how to define a component.
In the code below, we define a function called Header
that returns the same JSX that we had above for the header:
function Header() {
return (
<header>
<div className="logo">The logo</div>
<nav>The nav</nav>
</header>
);
}
This Header
function is a component, more specifically a function component. As its name suggests, it represents a header.
Now if we want to be able to render this component into the webpage, we have to provide it to React.createElement()
. (Yes, the same React.createElement()
method we saw above.)
The component goes as the first argument to the method, but keep in mind that we don't call the component function, just provide its reference.
Here's how we'd render the Header
component into the #root
element:
import React from 'react';
import ReactDOM from 'react-dom/client';
function Header() {
return (
<header>
<div className="logo">The logo</div>
<nav>The nav</nav>
</header>
);
}
const component = React.createElement(Header, null);
const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(component);
React.createElement(Header, null)
serves to instantiate a concrete Header
instance (note that this is not OOP) and then the render()
method of root
renders this instance into the #root
element.
As we run this code, the dummy text 'The logo' and 'The nav' are displayed on the document, indicating clearly that the <header>
element has been rendered out on the document.
We can even inspect React's in-house DOM tree in React DevTools. Here's the tree for the code above:

First we have the Header
component and then, within it, the <header>
element with its <.logo>
and <nav>
elements.
React's virtual DOM tree does include component nodes, however when it's reconciliated into the HTML DOM, only the actual element nodes are rendered to the document. We'll see how exactly this works in the React Components chapter.
Anyways, moving on, just as we could replace React.createElement()
calls with the corresponding JSX elements, we could do so for components as well.
The same Header
component above could be instantiated as follows:
/* ... */
const component = (
<Header></Header>
);
const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(component);
The name of the component (Header
) is used to denote the starting tag (<Header>
) and the ending tag (</Header>
).
If we want to, we could even omit the ending tag (</Header>
) here, making sure that there is a /
at the end of the starting tag, as shown below:
const component = (
<Header/>
);
Once again, this concise <Header/>
syntax is borrowed from HTML itself.
However, unlike JSX, HTML doesn't strictly require us to put a /
at the end of tags that don't have a corresponding ending tag, such as <img>
, <link>
, <meta>
, etc.
/
at their end. For example, where in HTML, <img>
is valid, in XHTML, it's not. The correct way to write this in XHTML is <img/>
.Now that we know what exactly is a component and how to create one, let's get back to the idea of state in React.
State of components
The next exciting thing in React, after components, is that of the state. Whenever we talk about state in React, it's intrinsic to talk about components.
State simply describes a component, so to speak.
If you have experience of object-oriented programming in JavaScript, you'd probably already be familiar with the idea of state. The state of an instance (of a class), which is merely a set of properties on it, describes the instance.
The idea of state is even present in HTML and CSS. For example, when you hover with the mouse pointer over an element, its state changes to :hover
. Similarly, if you focus a given input, its state changes to :focus
. And once again, the state describes the element at a certain point in time, i.e. either the pointer is over the element or the element has focus.
In user interfaces, the state of a given element describes that element, which in turn affects its visual appearance and possibly even its behavior.
The concept of state in React is not any different from this.
To define the state of a component in React — or more precisely speaking, to define the state of a function component — we use the useState()
hook.
A hook is simply a way to 'hook' a component to a given functionality.
useState()
hook enables a component to hook into the state utility of React.useState()
is defined on the imported React
object.
The state data is simply provided as an argument to useState()
. The data can be literally any value in JavaScript — a number, a string, a Boolean, undefined
, null
, an array, a regex, an object; just about anything!
The most interesting part of working with useState()
is its return value.
useState()
returns an array whose first element is the value of the corresponding state data and whose second element is a function to update the state to a new value.
Consider the following code where we try to store the text to be shown inside the <h1>
element as state data of the Heading
component:
function Heading() {
const [text, setText] = React.useState('Hello World!');
return (
<h1>{text}</h1>
);
}
The most important statement here is in line 2:
useState('Hello World!')
adds a single state datum to the createdHeader
component. The initial value of this datum is the string'Hello World!'
.useState()
returns back an array whose first element is this state datum and the second element is a function to update this datum.text
is merely a convenient name that we give to the state datum. We could name it anything (it's just a variable) —content
,str
,h1Text
,html
...setText()
is the name of the local function that we could use to update this state datum.
Note that it's conventional to name the state-update function starting with the word 'set', followed by the name of the state variable.
For example, in the case above, the variable was named text
, likewise the function was named setText()
(obviously after updating the casing of the state variable's name, i.e. going from text
to Text
in setText
).
Similarly, if the state variable was firstName
, then, abiding by this convention, the corresponding state-update function would be named setFirstName
.
Following this convention, yet again, what would the state-update functions for the state variables content
and isThemed
be called?
setcontent
andsetisThemed
set_content
andset_isThemed
setContent
andsetIsThemed
Anyways, once we have the state variable and the state-update function with us, we could use them both to make our component interactive, thanks to the state-update function.
In React, when the state-update function is invoked, it automatically triggers a rerender (similar to what we were doing manually by calling the render()
method of our root
object in the sections above).
Let's use text
and setText()
from the previous code to power a click
handler on <h1>
, similar to what we did in the previous sections above:
function Heading() {
const [text, setText] = React.useState('Hello World!');
return (
<h1 onClick={() => { setText('useState() in action.') }}>{text}</h1>
);
}
Inside the onClick
handler, we call setText()
with the new text value to be put inside <h1>
.
Open up the link above, and then click on the <h1>
element. You'll notice a change upon the click, all thanks to setText()
.
Let's see what exactly happens when setText()
gets called:
- The previous state datum, which was
'Hello World!'
, is replaced with the value'useState() is amazing'
. - The
Header
component is rerendered and, likewise, invoked again. - Inside the
Header()
function,useState()
executes again, this time returning an array whose first element is the latest state data, i.e.'useState() is amazing'
, and the second element is the same state-update function. - This new state data obviously gets stored in
text
. text
is rendered inside the<h1>
element.
See how simple this is compared to manually invoking root.render()
, as we did in the previous section.
This is how React is designed to be used. This is how React thinks about dealing with UI construction.
The setText()
function above is reactive. That is, as soon as we call it, a new render is triggered automatically — a reaction is made — and the UI, thereafter, updates as if by magic.
Purely commendable.
Instead of accessing useState()
as React.useState()
, we can simplify this by using a named import while importing in the 'react'
package.
So the code above could be expressed more compactly as follows:
import React, { useState } from 'react';
import ReactDOM from 'react-dom/client';
function Heading() {
const [text, setText] = useState('Hello World!');
return (
<h1>{text}</h1>
);
}
const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<Heading/>);
Notice the { useState }
named import in line 1 — it serves to introduce a variable called useState
into the module, holding the React.useState()
function.
In React applications, this named-import approach is quite mainstream. That is, instead of accessing hook functions as React.hookFunction()
, the functions are imported directly while importing the 'react'
package, and thereafter used as hookFunction()
.
From this point onwards, we'll follow this very import convention when wanting to use hooks.
Moving on
This chapter touched on a lot of different ideas in React, which are fundamental for you to understand to be able to effectively build complex React apps without running into glitches.
However, there are tons and tons of intricate details left off deliberately for the sake of brevity. Clearly, if we were to introduce everything rightaway at this beginning stage, it would've been way overwhelming and tedious to follow.
We'll explore all of this intricacy, to its very core, in the upcoming chapters.
For now, it's super important that you experiment with useState()
to implement such kinds of elementary programs. They may seem elementary, for sure, but the more you code them, indeed the better you'll become in React.
The next two exercises get you to code such programs using all of the knowledge that you obtained thus far in this course. After the exercises, we'll move over to unravel the power of JSX and explore it in extreme detail.