Introduction
So the last chapter, React Prerequisites, talked about the necessary concepts to know before one can begin his/her React journey. It touched upon very important ideas such as let
and const
; arrow functions; destructuring; closures; reflow and repaint; and much more.
Now we're almost ready to start writing React. Yup, almost. In this chapter, we'll see how to set up the entire environment for writing and executing React in Visual Studio Code. We'll see two common approaches to get started — one is the quick way and will get you started in almost no time while the other is the standard one used in real-world app development.
We'll learn each and everything about the setup and refrain from using ready-made toolchains such as Create React App to be able to understand what exactly goes behind the scenes when working with real-world React apps.
The quickest way
The quickest way to get started with React is to use external CDN libraries accessible from unpkg.com. One library is for React's core, while the other is for React DOM (the rendering library of React for the browser).
With this approach, we don't have to install literally anything on our end to get started with React.
Let's say we have the following barebones HTML page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Learning React</title>
</head>
<body>
</body>
</html>
To get started with React in this way, we just need to bring in two external <script>
s from unpkg.com, as illustrated below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Learning React</title>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
</head>
<body>
</body>
</html>
With these scripts in place, we can now go on and include a <script>
tag in <body>
, right at the end, where our React code will eventually go:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Learning React</title>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
</head>
<body>
<script>
// React code goes here.
</script>
</body>
</html>
The reason for putting this <script>
tag in the end is just so that if we want to select any DOM nodes from the <body>
element, we have access to it in our React code.
<script>
tag in the <head>
section (obviously after the preceding two <script>
tags), then we won't have access to the <body>
element, let alone having access to any element inside <body>
.So far, so good.
Although, the code above is indeed all that is required to run React, it isn't enough to be able to parse JSX in our React code. As we shall see later on when we learn more about JSX, React developers almost always write their code using JSX. It makes React very very intuitive to work with.
The problem with JSX is that it can't be understood by browsers — it's purely a syntactic innovation. And so to get it to execute on a browser, we have to use another program that itself runs in the browser and is capable of understanding and executing the JSX code we write.
Babel is such a program.
It is capable enough to parse many modern features and innovations of JavaScript, including JSX, TypeScript and the beta features of ECMAScript (sometimes referred to as ESNext).
And just like React, Babel is also available as an external library at unpkg.com, which means that we could easily bring it into our HTML via a <script>
tag.
Let's go ahead and refer to the Babel library in our HTML page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Learning React</title>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<script>
// React code goes here.
</script>
</body>
</html>
And we're done, right? Well, not now.
As stated before, JSX can't be understood by a web browser. If we continue with the setup above as it is, the <script>
inside <body>
, which is meant to contain our React code expressed in JSX, will get executed just like any other <script>
. But since a browser can't understand JSX, this will lead to an error.
Yes, Babel has been brought in and is running, but it doesn't have control over being able to prevent this script from running. We have to explicitly instruct the browser to do so, ourself.
And that's typically done by adding a type
attribute to the <script>
tag whose value is NOT recognizable by the browser. This is a behavior common to all browsers whereby when they encounter a <script>
with an unknown type
, they don't execute it.
Coming back to Babel, it specifically says to set the type
of the script, that it should be executing, to "text/babel"
. In this way, the Babel program is able to detect which <script>
it needs to execute and which not.
<script>
s in there and checks each one's type
attribute (which can very easily be done using DOM methods). If the type
is equal to "text/babel"
, Babel parses and executes that <script>
.Below, we make this desired change in our HTML page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Learning React</title>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<script type="text/babel">
// React code goes here.
</script>
</body>
</html>
And this is it for the most straightforward way of getting started with React.
If you want to, you could skip the following sections and move over to the next chapter, writing your very first React program.
However, almost no one develops applications this way.
It might suffice as a quick way to get started with React, but in the longer run, it's not going to be scalable, maintainable or effective. It's not even resource-wise efficient for production.
The problem with calling Babel at runtime
Everytime we refresh the webpage, the Babel program has to reparse our entire React code and only then be able to execute it. If our code doesn't change, this can be wasteful.
Moreover, if the code is extremely huge — think of something of the scale of Facebook or Spotify — then it could take some time to parse and execute it. That time might just be milliseconds, but in today's modern era, even a single millisecond matters, especially if we know that we could easily improve upon that.
And even if time isn't a concern somehow, such an approach is always bound to waste to resources. Ideally, the parsing of the code should happen once and then the result be used again and again. Not only does this prevent the wastage of resources but also gives a boost to speed.
So then what?
Well, as is the norm, we'll set up a local environment to work with React that allows us to easily manage complex React projects for both development and production, and also solves the problem discussed above of running Babel directly in the browser.
The following discussion is devoted to understanding this setup.
What are bundlers?
When companies work on complex projects, they always break down those projects into individual files (commonly referred to as modules). This is necessary for maintainability, scalability and, most importantly, easy collaboration. But this isn't just limited to teams or companies; it applies on a personal level as well.
When we build complex personal projects — think of a blog app, or a todo app, or a simple music player — we also tend to break down our projects into individual files for similar reasons. It's extremely challenging, if not near impossible, to work in one single code file.
Now when we come into the world of JavaScript, modularization of our projects is good, as in all languages, but not without a slight issue. Remember that we're talking about web apps here and those are served over HTTP.
Modularization, by breaking down the project into multiple files, is clearly handy but it comes at a cost: the cost of loading times of the app.
The more modularized our code base is, that means the more files we need to call in on the client's end to get the app to run; which in turn means the more number of HTTP requests required to be dispatched and their responses consequently parsed; and ultimately, the longer the loading time of the app.
We just can't ignore the effects that loading multiple files have on the latency of a web app.
Now the best solution to this seems to be to not modularize our JavaScript at all, or do it sparingly. We should instead put all of our code in one single file and deliver this single file to the client. End of story.
But do we really have to do so?
Well, it would've been exactly this way had it not been for bundlers.
A bundler essentially does the same thing that we mentioned a moment ago, i.e. putting all of our code in one single file. But it does this without us having to do so. It does this without us having to keep ourselves from modularizing our project.
Bundlers offer benefits to both: an app's developers and its users.
For developers, they could continue modularizing their projects as much as they need to. No limits. They could even minify the bundle's JavaScript files to make them extremely terse and, thus, save on more bytes of network usage otherwise wasted in delivering those parts of code that have no effect on its final output.
For users, they get much better loading times of the app since only a single file is requested for and thereafter delivered to their devices, and also less to almost no wastage of their resources.
Simple.
In the JavaScript community, by far the most popular bundler is Webpack.
Webpack is a complex beast in itself. There are whole books written purely for just configuring and working with Webpack — that's its complexity, configurability and potential!
Besides Webpack, however, there exist a couple other bundlers as well, lighter in weight and complexity and suitable for small projects like the ones we'll be creating throughout this course.
The bundler that we'll be using in this course is Rollup.

Rollup is an amazing alternative to Webpack, and evolving to become better than ever. The official documentation site, rollupjs.org, explains the tool in an extremely nice way, definitely worth a read.
Let's see how to install and set up Rollup, specifically for React applications. This setup is all that we need to get the whole environment to code React ready to fire.
Setting up the environment
Before we begin, it's important for you to have Node.js installed on your machine, including its default package manager, NPM.
We'll be needing both of these for installing Rollup and some other amazing tools too.
Besides this, it's also required that you have Visual Studio Code installed on your computer. Visual Studio Code is one of the most popular and powerful open-source editors for coding, and the one that we'll be using throughout this entire course.
-
Create a new workspace in VSCode
When we work on a given project, we don't work with haphazardly arranged files, scattered across our computer. No. Almost always, we work on a project inside a directory created to hold everything related to the project.
As you start building React applications, you'll do the same.
So, go on and create an empty directory on your filesystem, where you find it easy to access, and name it react-course (all small-caps).
We'll create the directory on the Desktop, as shown below, using a quick
mkdir
command:C:\Users\CodeGuage\Desktop> mkdir react-courseThe same thing could, obviously, also be achieved using the graphical interface.
Once the directory is created, we need to open it up in Visual Studio Code as a new workspace. A workspace is basically a directory holding all the work of a project.
To open up a directory as a workspace, launch Visual Studio Code, and then from the Get Started window that shows up, select the Open Folder link. This will open up a file browser panel. From here, locate the react-course directory that you just created and then open it up.
This should create the directory as a new workspace in VSCode, ready to be worked with.
Let's move to the next step.
-
Create a package.json file
With the react-course directory created, next we'll create a new package.json file in it.
The purpose of a package.json file is fairly straightforward. As we build our project and install nmerous packages to drive it, package.json acts like the proejct's documentation, collecting the names of all the packages installed. This makes our project portable.
For instance, if we plan to publish the project to GitHub, then we only need to publish the actual code of the project along with package.json, NOT the installed packages. Anyone could then easily clone the GitHub repo on his/her machine and just run
npm install
to download the same packages that you were using on your end.Not only this, but package.json also documents the project being created, such as listing its version, the name of its author(s), its short description, its license, and so on. It's much more than just a mere JSON file — it's the core of NPM (and other package management systems as well).
Now, you could create a package.json file yourself and then add the necessary details into it, or you could let
npm
do this for you by running the commandnpm init
.npm init
serves to initialize a new project by defining a package.json file for it in the current working directory. It asks a couple of questions from us regarding the project, which we can either fill out or leave out and go with the default values, in order to describe our project.If you want to completely skip this questionaire phase, append a
-y
flag to the command. This simply means that all the defaults should be used for the package.json file.npm init
is a really quick and handy way to get started with a project in no time.Below we run this command inside our react-course directory:
...\react-course> npm init -yWrote to ...\react-course\package.json: { "name": "react-course", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }Now let's open up the package.json file created:
{ "name": "react-course", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
Everything is the exact same as mentioned in the console output. Perfect!
Let's move on.
-
Install Rollup
Once package.json is created, we then need to install the bundler tool Rollup, represented by the
rollup
package on NPM.Go ahead and type the following command in the console to install Rollup:
...\react-course> npm install rollupIt launches an installer in the console, downloading Rollup, and then ends with the bundler installed locally inside the react-course directory.
Also, as the command completes, our package.json file gets updated automatically to consider
rollup
as one of its dependencies.What is a dependency?
A dependency is simply anything that our project depends on.
In the case above, our projects depends on the
rollup
package, hence we say thatrollup
is a dependency of the project.It's because of these dependency lists that we are able to portably transfer projects containing package.json. The other person using our project only needs the project files and package.json to replicate the same set of tools, that we've using for our project, on his/her end.
Isn't this amazing?
Here's the updated package.json file:
{ "name": "react-course", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "description": "", "dependencies": { "rollup": "^3.14.0" } }
Notice the
dependencies
object. It listsrollup
as a dependency along with a description of which version we're using. -
Install Babel
We saw Babel in action above when we called on an external Babel library in our HTML page to parse and execute the
<script>
tag supposed to hold our React code.In this manner, Babel was acting as a runtime engine. It was parsing and executing our React code, all on its own. But as we stated before, there's a cost of doing all this parsing on the browser. A much better approach is to parse the code and transpile all of it upfront to a syntax that can be directly executed by the browser, and do this just once.
Fortunately, Babel can do this as well!
Babel can be, and is customarily, used as a transpiler, converting innovative syntax into normal JavaScript that all modern browsers can easily understand.
In our Rollup setup, as you'll see in the next section, we'll be using Babel as the transpiler to help convert JSX code into the corresponding parseable JavaScript.
But before that, we first need to install Babel just like we installed Rollup above, and even before that we need to understand how Babel is organized.
Babel is divided into multiple pieces.
- The core piece is represented by the package
@babel/core
. - With this core piece, Babel provides us with numerous plugins to install depending on whether we want a particular feature.
For instance, if we want to write code for old browsers that don't support arrow function, but still use them, we can use the plugin
@babel/plugin-transform-arrow-functions
.Managing multiple plugins can quickly become cumbersome and that's why Babel provides us with predefined collections of plugins in the form of presets.
The preset that we're interested for now is
@babel/preset-react
.Let's go on and install both of these packages, which will eventually be used by our Rollup bundler:
...\react-course> npm install @babel/core @babel/preset-reactGreat.
Let's move on.
- The core piece is represented by the package
-
Install core React and React DOM
Recall from the quick setup above, akin to Babel, we called in two external React libraries in our HTML page to be able to write React code in it. One was for React core while the other one was for React DOM.
Now, since we've left the comfort of these external scripts, we'll have to manually install them both in our react-course directory.
The React core library is represented by the
react
package while the React DOM library is represented byreact-dom
.Enter the following command to install both of these packages at once:
...\react-course> npm install react react-domGreat again.
Over to the next step.
-
Configure Rollup
Let's quickly review what packages we've so far installed in our project. We started off with
rollup
, then installed@babel/core
and@babel/preset-react
, and then finished off withreact
andreact-dom
.We indeed have all the tools that we need to be able to write and run React. But the tools haven't still been integrated together. That's what we're about to do in this section.
Like most tools have their own ways of representing their configuration files, Rollup has one too. It's called rollup.config.mjs.
As you can see, it's a JavaScript file; not a JSON file (like
package.json
). This is so that we have the flexibility and power of working in JavaScript while making our Rollup configurations.We'll be needing to customize Rollup to be able correctly parse and then bundle React code, so let's go on and create a rollup.config.mjs file in our react-course directory:
rollup.config.mjs// Rollup's configurations
The syntax of this file is pretty basic. All the configurations that we need to make are put in the default export object.
The three main properties of this object are as follows:
input
— specifies the entry file for the bundling process.output
— specifies information regarding the final bundle, such as its path and its format.plugins
— a list of plugins to use in the bundling process.
In our case, we'll go with a conventional approach.
We'll create a directory public in react-course that holds all our public files, i.e. the ones that users can actually see, and then create another directory called src, once again in react-course, that holds our app's (modularized) source code.
Inside src, we'll create a file called index.js. This is where our React code will go. Moreover, the location of this file will go inside the
input
property shown above.The destination file of the bundle will be called bundle.js and be inside the public directory. There's actually no need to create this file manually because Rollup will automatically do it during the bundling, given that we configure the
output
property correctly.So far, here's the rollup.config.mjs that we reach:
export default { input: 'src/index.js', output: { file: 'public/bundle.js', format: 'iife' } };
Next up, now we need to install four plugins for Rollup so that it's able to amalgamate all the tools that we've installed so far above.
Each of these plugins is listed and described as follows:
@rollup/plugin-node-resolve
is used to get Rollup to use Node's module resolution algorithm. This is necessary so that we could easily import thereact
andreact-dom
packages that we just installed.@rollup/plugin-commonjs
is used to import CommonJS modules. By default, Rollup uses ECMAScript modules and so to change this, we require this plugin.@rollup/plugin-babel
is used to integrate Babel with Rollup so that it is called on in the bundling step.@rollup/plugin-replace
is used to replace given pieces of text from the files in the bundle with other pieces of text.
Let's quickly install all four of these plugins:
...\react-course> npm install @rollup/plugin-node-resolve @rollup plugin-commonjs @rollup/plugin-babel @rollup/plugin-replaceNow, let's use these plugins in our Rollup setup by importing them in our rollup.config.mjs file.
As stated before, Rollup first needs to know how to search given modules in the current project and that's done using the
node-resolve
plugin.Below we first import the plugin and then use it inside the
plugins
array (note that Rollup plugins ought to be called since they are functions):import nodeResolve from '@rollup/plugin-node-resolve'; export default { /* ... */ plugins: [ nodeResolve({ extensions: ['.js', '.jsx'], }), ] };
The extensions property of the object passed into
nodeResolve()
specifies the file extensions on which thenode-resolve
plugin should intervene. In our case, there will be two extensions: .js and .jsx.Once Rollup is done locating a module using
node-resolve
, it needs to read and parse its contents. As per the design of Rollup, it doesn't understand CommonJS modules by default — we have to manually make it do so. At the same time, React's libraries are all written in the CommonJS style.Likewise, we need to use the
commonjs
plugin to get Rollup to be able to understand CommonJS modules.This is done below:
import nodeResolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; export default { /* ... */ plugins: [ nodeResolve({ extensions: ['.js', '.jsx'], }), commonjs() ] };
The next step after Rollup parses the module's code is to tell it to convert the JSX code (recall that we'll be using Rollup to ultimately bundle React code) into normal JavaScript that the browser could understand.
For this, we need to use the
babel
plugin and customize it as follows:import nodeResolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import babel from '@rollup/plugin-babel'; export default { /* ... */ plugins: [ nodeResolve({ extensions: ['.js', '.jsx'], }), commonjs(), babel({ babelHelpers: 'bundled', presets: ['@babel/preset-react'] }) ] };
The
presets
property of the object passed tobabel()
specifies the presets that Babel should use in the bundling process. Since we just need to convert JSX code into normal JavaScript, we only refer to the preset@babel/preset-react
that we installed above.If we want to additionally convert the normal JavaScript (which includes ES6+ features) to old JavaScript that is supported by old browser, we can additionally use the preset@babel/preset-env
.The final step is to perform minute processing in one of the libraries of React to remove a call to
process.env.NODE_ENV
.As you might know,
process
is a global identifier exclusively available only in Node. If we use it in a browser, it'll just returnundefined
, and soprocess.env.NODE_ENV
in the browser will lead to an error.To keep our bundle from throwing this same error, we ought to instruct Rollup to find this piece of text and replace it with the literal string
'development'
. And this demands the usage of thereplace
plugin.Let's add this plugin to our configuration file:
import nodeResolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import babel from '@rollup/plugin-babel'; import replace from '@rollup/plugin-replace'; export default { /* ... */ plugins: [ nodeResolve({ extensions: ['.js', '.jsx'], }), commonjs(), babel({ babelHelpers: 'bundled', presets: ['@babel/preset-react'] }), replace({ preventAssignment: false, 'process.env.NODE_ENV': 'development' }) ] };
The
preventAssignment
property is used to instruct thereplace
plugin whether to prevent replacements in variable assignments. The valuefalse
obviously means not to prevent it.All the subsequent properties define replacements. Each property's key specifies the text to replace while its corresponding value specifies the new text.