React Manual Setup

Chapter 4 41 mins

Learning outcomes:

  1. Modularization in JavaScript
  2. What is a bundler
  3. Manual React setup using Rollup

Introduction

In the last chapter, we saw how to quickly set up a React development environment in either of two ways: by calling in external <script>s on a simple HTML page or by using the Create React App tool.

Now in this chapter, which is an optional read, we shall see how to manually set up such an environment. We'll go through a multitude of concepts, including what are bundlers, the role of Babel, what exactly are react and react-dom, configuring npm scripts, and so on.

Honestly speaking, configuring React manually isn't that hard as it might seem like. In fact, it's recommended that every single React developer knows what goes behind the curtain when he/she is using a preconfigured, scaffolding tool like CRA.

This knowledge is key to being able to eject from such scaffolding tools and tune them according to our needs. Without a thorough understanding of these technicalities, we are just left to count on these preconfigured tools.

Modularization in JavaScript

When we build complex personal projects — think of a blog app, or a todo app, or a simple music player — it's a common pratice to break down our projects into individual files, or modules. This is often referred to as modularization.

It's exceptionally challenging to work in one single code file that's spans thousands of lines. Yes, we wouldn't have to constantly switch between different files in this case but the length of that one single file is enough to keep as away from even touching the code.

Modularization allows us to work with small individual files, one at a time, and then amalgamate them together to breathe life into a project.

There are multiple benefits of modularization:

  • It allows for reusability. A repeated set of utilities could be put inside a module and then that module used wherever those utilities are needed.
  • It allows for collaboration. One developer can work on module A, one can work on module B, another can work on module C, and so on.
  • It significantly improves maintainability. Finding bugs and fixing them becomes way much easier when developers have smaller files to look into and isolate for testing.

Almost all popular languages recognize the importance of modularization and the fact that those languages would be used to create complex projects, and likewise provide modularization features out of the box.

Talking about JavaScript, even though it didn't begin with robust modularization features, after the advent of ES6, this finally became a reality in the language.

Modularizing our JavaScript 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.

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.

Apart from this, since ES6 modules are still a relatively recent feature, some old browsers don't support them. This means that if we use modules in our projects, we would be missing on some old browsers, which might be an issue if we want our apps to work correctly on those browsers as well.

Now the best solution to this seems to be to not modularize our JavaScript at all, or do it sparingly. Right?

Well, it would've been exactly this way had it not been for bundlers.

What is a bundler?

A bundler does a very simple job, already apparent in its name:

A bundler takes a bunch of JavaScript files (or better to say, modules) and bundles them together into one single file.

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.

That is, when using a bundler, we don't have to write our JavaScript code all in one single file (by virtue of the worry that modularization would lead to network latency or browser incompatibility); instead we can keep modularizing our code base while the bundler amalgamates it into one single file.

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

Bundlers offer other benefits as well that go way beyond just modularization.

For example, bundlers can be used to minify JavaScript files as they are bundled together to save on countless of bytes and, thus, reduce file sizes. Bundlers can also be used to perform code splitting, which in essence is to call on additional scripts only when they are required by a web application.

In short, bundlers are just amazing!

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 level of complexity and configurability!

Besides Webpack, however, there exist a couple other bundler tools as well, lighter in weight and complexity, and suitable for simple projects like the ones we'll be creating throughout this course.

The bundler that we'll be using here is Rollup.

Rollup's official documentation site.
Rollup's official documentation site.

Rollup is an amazing alternative to Webpack, and evolving to become better than ever. The official documentation site explains the tool in an extremely nice way — surely worth a read.

Let's see how to install and set up Rollup, specifically to be able to develop React applications.

Manual setup using Rollup

Once again, before we begin our manual setup, it's important for you to have Node.js installed on your computer — for all our individual tools will be installed using npm.

  1. Create a new react-course directory

    With CRA, we supply it with the name of the directory to represent our React project and then it itself creates and configures everything in that directory.

    Without CRA, however, we have to create the directory ourselves.

    So, first things first, let's go on and create a react-course directory, again under Desktop in Windows. We'll do so using the terminal:

    ...\Desktop> mkdir react-course

    This command will create an empty directory named react-course in Desktop.

    Obviously, the same thing could also be achieved using the graphical interface on Windows, i.e. by right-clicking on the Desktop and then creating a new folder using the context menu.

    Great! Let's proceed to the next step.

  2. Create a workspace in VS Code

    With the directory created, we need to open it up in VS Code as a workspace. To restate it, a workspace in VS Code is basically just a directory holding all the work of a project.

    To open up a directory as a workspace, launch VS 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 VS Code.

    Over to the next step.

  3. Create a package.json file

    With the react-course directory created, next we'll create a package.json file in it.

    What is package.json?

    The purpose of a package.json file is fairly straightforward.

    As we build our project and install numerous packages to drive it, package.json acts like the project'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 command npm init.

    npm init serves to initialize a new project 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. In the end, npm init creates a package.json file in the 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.

    Below we run this command inside our react-course directory:

    ...\Desktop\react-course> npm init -y
    Wrote to ...\Desktop\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" }

    At this stage, our VS Code workspace should look as follows:

    package.json in our workspace.
    package.json in our workspace.

    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.

  4. Install Rollup

    Once package.json is created, we 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:

    ...\Desktop\react-course> npm install rollup --save-dev

    It launches an installer in the console, downloading Rollup, and then ends with the bundler installed locally inside the react-course directory, under a node_modules directory.

    Also, as the command completes, our package.json file gets updated automatically to count rollup as one of its dependencies, specifically as a developer dependency.

    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 that rollup is a dependency of the project.

    package.json keeps track of all the dependencies of our projects. It's because of these dependency lists maintained by package.json that we're able to portably transfer projects containing a package.json file.

    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?

    What is --save-dev?

    The --save-dev flag in the command above is used to instruct npm to treat the underlying dependency as a developer dependency.

    A developer dependency is a dependency that only applies while developing a program. That is, a developer dependency isn't used when actually running an application in the browser.

    rollup is only used when developing our React application, hence it's installed with this --save-dev flag set.

    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": "",
    
    "devDependencies": {
    "rollup": "^3.14.0"
    } }

    Notice the devDependencies object. It lists rollup as a dependency along with a description of which version we're using.

  5. 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 performance cost of doing all this parsing on the browser.

    A much better approach is to parse the code and transpile 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-to-next step, we'll be using Babel as the transpiler to help convert JSX code into 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 in npm.

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

    For instance, if we want to write code for old browsers that don't support arrow functions, we can use the plugin @babel/plugin-transform-arrow-functions which will help convert them to regular anonymous functions.

    Managing multiple Babel plugins can quickly become cumbersome because there are too many of them, and that's why Babel provides us with predefined collections of plugins in the form of presets.

    The preset that we're interested in for now is @babel/preset-react.

    @babel/preset-react contains all the necessary plugins to be able to convert JSX code into normal JavaScript code.

    Let's go on and install both of these packages, which will eventually be used by our Rollup bundler:

    ...\Desktop\react-course> npm install @babel/core @babel/preset-react --save-dev

    Great.

    Let's move on.

  6. Install core React and React DOM

    Recall from the first section on this page that we called in two external <script>s 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.

    We need both of these libraries in our manual setup as well.

    The React core library is represented by the react package while the React DOM library is represented by react-dom in npm.

    Enter the following command to install both of these packages at once:

    ...\Desktop\react-course> npm install react react-dom

    Notice that we don't have the --save-dev flag here. This is because both react and react-dom are used while running a React application in the browser; they aren't just used during development.

    Over to the next step.

  7. Configure Rollup

    Let's quickly review what packages we've installed so far in our project. We started off with rollup, then installed @babel/core and @babel/preset-react, and then finished off with react and react-dom.

    At this point, we indeed have almost 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.

    Go on and create a rollup.config.mjs file directly inside react-course:

    rollup.config.mjs file in VS Code Explorer.
    rollup.config.mjs file in VS Code Explorer.

    So what exactly is this?

    Like most tools have their own ways of representing their configuration files, Rollup has one too. It's called rollup.config.mjs.

    The .mjs file extension is used because our configuration file would be using ECMAScript modules instead of traditional CommonJS modules used in Node.js. If we were to use the traditional CommonJS modules, we'd be naming our file as rollup.config.js.

    This is a JavaScript file, not a JSON file (like package.json), simply so that we have the all the power and flexibility of JavaScript while making our Rollup configurations.

    Now, let's roll up our sleeves and get working to configure Rollup to be able to run React code.

    We've already covered how to use Rollup to set up a React development environment in this article from our blog. In order to keep this chapter short, we defer the setup of Rollup here to the Installing Rollup and its plugins section of the given article.

    So before you proceed forward, get Rollup configured by going through the following link:

    Installing Rollup and its plugins

    After you're done with this, your react-course directory should look as follows:

    react-course directory after Rollup has been configured.
    react-course directory after Rollup has been configured.

    And your rollup.config.mjs file should look as follows:

    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 {
       input: 'src/index.js',
       output: {
          file: 'public/bundle.js',
          format: 'iife'
       },
       plugins: [
          nodeResolve({
             extensions: ['.js', '.jsx']
          }),
          babel({
             babelHelpers: 'bundled',
             presets: ['@babel/preset-react'],
             extensions: ['.js', '.jsx']
          }),
          commonjs(),
          replace({
             preventAssignment: false,
             'process.env.NODE_ENV': '"development"'
          })
       ]
    }

    If everything on your end is the same, wonderful!

    Here's the index.js file from our react-course directory:

    The index.js file in our file.
    The index.js file in our setup.

    It's empty and waiting for some code to be added to it, and that's exactly what we'll be doing in the next chapter. Let's keep going.

  8. Configuring a build npm script

    Open up the package.json file and head over to the section that reads scripts. In this step, we'll be configuring a manual npm script so that we can very conveniently launch Rollup's bundling process.

    We'll call the script build because Rollup actually 'builds' a bundle by amalgamating all linked modules. This script will be run by entering the command npm run build into the terminal.

    As for the script itself, it'll simply be rollup -c -w. Let's understand what's happening here:

    • rollup invokes the rollup package that we installed a while ago — it's the very heart of Rollup.
    • The -c flag instructs Rollup to use a configuration file in the underlying directory instead of expecting for the configurations to be provided in the terminal.
    • The -w flag instructs Rollup to watch for any changes to the project and re-bundle if any file therein get modified.

    In the following code, we define the build script:

    {
       "name": "react-course",
       "version": "1.0.0",
       "main": "index.js",
       "scripts": {
    
    "build": "rollup -c -w" "test": "echo \"Error: no test specified\" && exit 1" }, ... }

    With the script created, now go on to the terminal again, and enter the command npm run build:

    ...\Desktop\react-course> npm run build

    This will commence the bundling process, which might take a few seconds to complete. Once complete, we'll have a bundle.js file in the public directory, containing our bundled code.

    Moreover, thanks to the -w flag, the process will keep running, watching for changes in our project files and then automatically re-running the bundler in the instance of any file changing.

    Obviously, since our index.js file is empty right now, the bundle would be empty as well. In the upcoming chapters, however, when we finally write some React code, our bundle would have a lot of code in it.

    Anyways, it's time for the final step.

  9. Installing serve and configuring npm start

    While Rollup bundles our React project files that resides under src into a single file inside public, we still don't have anything to serve our index.html file from a server.

    That is, currently the only way to view the index.html file is to open it up as a mere file from the filesystem (using the file URI scheme).

    However, React discourages developing applications in this way. The recommended approach is to use a web server, serving up the index.html file (using the http URI scheme).

    Fortunately, using the serve package from npm, it's really simple to spin up a web server, serving up our public directory.

    First off, let's install serve from npm as a normal dependency:

    ...\Desktop\react-course> npm install serve

    Next up, let's create an npm start script in our package.json, invoking the serve package. The invocation is pretty simple; just call serve ./public to serve the public directory over http://localhost:3000:

    Following we define the start script:

    {
       "name": "react-course",
       "version": "1.0.0",
       "main": "index.js",
       "scripts": {
    
    "start": "serve ./public" "build": "rollup -c -w" "test": "echo \"Error: no test specified\" && exit 1" }, ... }

    Now, go on and run the following command in the terminal (assuming that the previous command npm run build is still running in another terminal):

    ...\Desktop\react-course> npm start

    This will launch a web server, serving the public directory over http://localhost:3000. Open up the browser and navigate to this URL to see the index.html file in the public directory in action.

    Once everything is done, close all the terminals, for you have successfully seen how to launch a React application with this manual setup.