React Hello, World without Compilation

In this post I am going to walk through how I set up React for a web app with no compiltation step.

I’ll start by creating a directory called ‘react-hello-world’ and moving into it.

mkdir react-hello-world
cd react-hello-world

Next up I’m going to create an index.html as the default html file for a webapp is traditionally called. This is because most web servers render this file at the ‘/’ path by default so you don’t have to write www.example.com/index.html and can just write www.example.com.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  
</body>
</html>
index.html

If you open this file up in your browser (with open index.html on Mac or explorer.exe index.html on windows) you will see a blank page. I like to check things as soon as I’ve written enough code to check them so that I catch any silly little mistakes as I make them. It’s a process not unlike TDD except I’m not writing the tests (yet).

Our application is going to depend on React. There are a number of ways we could keep track of those dependencies for example by linking to cdn version of the scripts we need, downloading them directly from official sources and using a package manager such as npm to handle the download. I’m going to opt for npm as I want the package manager to handle the dependencies for me and as the scripts will be downloaded to my local machine I will be able to develop my app offline. We’ll need to initalize npm before we install our dependecies by running:

npm init

This will generate a package.json file that will record some information about our project and more importantly, the libraries that our project depends on. Your package.json should look a lot like this:

{
  "name": "react-hello-world",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
package.json

I’ve left everything as default as I will customize the entries later, if I need to.

We will need to install React and a companion library ReactDOM. We can do this with:

npm install react react-dom

This will alter our package.json and append the dependency entries:

{
  ...
  "license": "ISC",
  "dependencies": {
    "react": "^16.8.4",
    "react-dom": "^16.8.4"
  }
}
package.json

We need ReactDOM as React only produces Virtual DOM (vDom) objects that represent the UI, it does not actually turn those objects into Document Object Modal (DOM) objects. Because we want to display our UI in the DOM of our browser we’ll use ReactDOM but we could use a different library to turn those vDom objects into instructions to display our interface on a different medium e.g. WebGL.

Though we’ve installed React and ReactDOM locally in our project, we haven’t included them in our webapp yet. To do that we’ll need to add some script tags to our index.html file.

The script tags we need are stored inside of the node_modules folder. If we run the command tree -L 1 node_modules you’ll see all of the libraries installed by npm.

$ tree -L 1 node_modules/
node_modules/
├── js-tokens
├── loose-envify
├── object-assign
├── prop-types
├── react
├── react-dom
├── react-is
└── scheduler

The folder we’re interested in are react and react-dom as these contain the scripts we’ll need to use those libraries.

They have a similar file structure, if we run tree -L 2 node_modules/react or tree -L 2 node_modules/react-dom we’ll see something like this:

$ tree -L 2 node_modules/react
node_modules/react
├── LICENSE
├── README.md
├── build-info.json
├── cjs
│   ├── react.development.js
│   └── react.production.min.js
├── index.js
├── package.json
└── umd
    ├── react.development.js
    ├── react.production.min.js
    └── react.profiling.min.js

Inside we can see all of the directories and files for the libraries. The directories umd and cjs stand for Universal Module Definition and CommonJS respectively and refer to the way in which the modules are defined (and so how they can be imported!).

Since we’re going to be using things in a browser and browser do not support cjs modules, we’ll need to add one of the umd/ scripts. Given that we are currently developing this Hello, World! application I have opted for the react.development.js file.

Adding the React and ReactDOM scripts to our index.html should result in this:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  <script src="./node_modules/react/umd/react.development.js"></script>
  <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
</body>
</html>
index.html

The ReactDOM script can be found in the same way as the React script. I’ve added them to the bottom of the page. We don’t use the defer or async attributes because our scripts are going to update the DOM so interupring the render is sensible.

The code we’ll use to render the “Hello, World!” to the page can be added with an inline script. This is not best practice for a large web app it will suffice for a simple script such as ours.

  ...
  <script src="./node_modules/react/umd/react.development.js"></script>
  <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
  <script>
    let root = document.body;
    let helloWorld = React.createElement('h1', {}, 'Hello, World!');

    ReactDOM.render(helloWorld, root);
  </script>
</body>
</html>
index.html

Here is our index.html after we’ve added the script to render “Hello, World!” to the page. Lets walk through this line by line.

let root = document.body;

Here we are simply defining a variable root which will be where our Hello World app is rendered. Using the body of the document as we are is not rcommended but I thought it would be worth finding out why that is, so lets use it anyway.

let helloWorld = React.createElement('h1', {}, 'Hello, World!');

This line defines a variable helloWorld which contains the vDom returned from the React.createElement function. In this case, the vDom represents a h1 tag with the text “Hello, World!” inside of it.

ReactDOM.render(helloWorld, root);

Finally this line takes the vDom for our Hello World app, converts it into DOM objects and inserts those DOM objects into our root element (the body).

If you run this you’ll see this:

What we see after loading index.html in the browser

And if we take a peek at the rendered DOM via the Developer Tools we will see:

The DOM after our app has been rendered with document.body as the root

You can see that the script tags we included have been removed and replaced with our Hello World app. Perhaps a good reason to use a root element when rendering our React applications 😉.

Lets refactor our code to make use of a separate root element so that our script tags and perhaps other tags in the body don’t get blown away!

The first change I’ll make is to use a getElementById query instead of just using the body.

let root = document.getElementById('root');

We’ll need to add an element to our html that has the id ‘root’ now. If we add a <div id="root"></div> to the html then we’ll end up with:

<body>
  <div id="root"></div>
  <script src="./node_modules/react/umd/react.development.js"></script>
  <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
  <script>
    let root = document.getElementById('root');
    let helloWorld = React.createElement('h1', {}, 'Hello, World!');

    ReactDOM.render(helloWorld, root);
  </script>
</body>
</html>
index.html

Now when React renders our app it won’t throw away our script tags. We can check in the dev tools again:

The DOM after our app has been rendered with <div id="root"></div> as the root

So here we’ve seen how to setup a very simple React project without a compilation step. Theres lots that I haven’t covered and if you’d like to see a lot more of how to work with React I suggest you go have a look at the official documentation which has a fantastic Hello World tutorial of it’s own!