Building a Node.js, React, and TypeScript App from Scratch

When building a React App, the create-react-app npm package builds a very modern setup in one command, ensuring all the underlying pieces work together seamlessly so that we don’t need to build and compose the whole toolchain ourselves.

The create-react-app works well, when you are new to React and want to learn the inner workings of React without having to worry about going through the complicated bootstrapping process.

It also works well with a simple blog or a small project that you want to quickly put together and wouldn’t need to override many configurations.

However, if you are working on an enterprise website or an app, you would want to build your project from scratch. This will not bloat your codebase with a lot of extra features that create-react-app comes with that your app doesn’t need or use. 

create-react-app also hides your webpack config under node_modules, making it impossible to add or update the configuration without ejecting the package – which is not recommended.

Using a Webpack module bundler instead allows you to have total control of your app. You can add dependencies and configurations as per your needs.

In this post, I will cover the process of building a basic React app from scratch. . 

Prerequisites: 

NPM and Node.js When you install node, npm will get installed automatically.

Here’s how you can create the app from scratch:

  1. Create a new directory for your project giving a name of your choice – eg. my-react-app
mkdir my-react-app
cd my-react-app
mkdir app
cd app
  1. Now initialize the project using the following npm command, this will create package.json in your root directory. The –y command will initialize the app with basic configuration and scaffolding for your project.
npm init --y
  1. Install the following app dependencies:
npm install --save react react-dom typescript 
npm install --save-dev webpack webpack-dev-server html-webpack-plugin webpack-cli  
npm install --save-dev @babel/core babel-loader @babel/preset-env @babel/preset-react @babel/preset-typescript css-loader style-loader

The flag --save will save the dependencies under the dependencies section of the package.json

The flag --save-dev will save the dependencies under the devDependencies section of the package.json. This will make sure these dependencies are not included in the production bundle – keeping the production build light and performant.

What are those dependencies?

  • React: A UI library for creating modular components
  • React-dom: Provides us with virtual DOM that React needs to render and re-render the components.
  • Webpack: A module bundler – it makes our application code usable in a web browser – often converting the entire package into a single bundle.js file which can be embedded into an HTML file easily and used by our application.
  • Webpack-dev-server: Transforms the react code and serves it with a development server that provides live reloading of the output in the browser, making development faster.
  • Html-webpack-plugin: This will generate an HTML5 file – index.html, that includes all our webpack bundles in the body using script tags.
  • @babel/core: A JavaScript transpiler that converts modern JavaScript into a backwards compatible version of JavaScript that all browsers could read.
  • Babel-loader: The webpack loader responsible for talking to Babel, it allows users to add custom handling of Babel’s configuration for each file that it processes.
  • @babel/preset-env: Out-of-the-box feature that bundles all the plugins needed to transpile all ES6 features into widely compatible syntax
  • @babel/preset-react: Out-of-the-box feature that bundles all React-specific Babel plugins responsible for converting JSX syntax into plain JavaScript that browsers can understand.
  1.  Create the app structure: folders and base files: Under the /app directory. complete the following steps:
mkdir src
cd src

            Create a file called index.html and copy the following code:

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>React Boilerplate</title>
</head>
 
<body>
    <div id="root">
 
    </div>
</body>
 </html>

Create another file called index.tsx and copy the following code:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
ReactDOM.render(<App />, document.getElementById('root'));

Create another file called app.tsx and copy the following code:

import React from "react";
import "./index.css"
 
const App = () => (
    <div>
        <h1>Hello World!</h1>
    </div>
);
 
export default App;

Create another file called index.css and copy the following code:

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}

Change directory and navigate to the /app folder.

Under the /app folder – create the following files:

  • .gitignore – a plain text file that contains the list of files and folders for git to ignore
node_modules
dist
build
coverage
  • .babelrc:
{
	"presets": [
		"@babel/preset-env",
		"@babel/preset-react"
	],
	"plugins": [
		"@babel/proposal-class-properties",
		"@babel/proposal-object-rest-spread"
	]
}
  • webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
	// webpack will take the files from ./src/index
	entry: './src/index',

	// and output it into /build as bundle.js
	output: {
		path: path.join(__dirname, '/build'),
		filename: 'bundle.js'
	},

	// adding .ts and .tsx to resolve.extensions will help babel look for .ts and .tsx files to transpile
	resolve: {
		extensions: ['.ts', '.tsx', '.js']
	},

	module: {
		rules: [{
				test: /\.bundle\.js$/,
				use: {
					loader: 'bundle-loader',
					options: {
						name: 'my-chunk',
						cacheDirectory: true,
						presets: ['@babel/preset-env']
					}
				}
			},

			{
				test: /\.(ts|js)x?$/,
				use: {
					loader: 'babel-loader',
					options: {
						cacheDirectory: true,
						presets: [
							["@babel/preset-env",
								{
									"targets": {
										"browsers": [">0.03%"]
									},
									"useBuiltIns": "entry",
									"corejs": 3
								}
							],
							"@babel/preset-typescript",
							"@babel/preset-react"
						]
					},
				},
			},


			// css-loader to bundle all the css files into one file and style-loader to add all the styles  inside the style tag of the document
			{
				test: /\.css$/,
				use: ['style-loader', 'css-loader']
			}
		]
	},
	plugins: [
		new HtmlWebpackPlugin({
			template: './src/index.html'
		})
	]
};

Update the package.json file with the following:

"scripts": {
    "bundle": "webpack --display-error-details",
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --mode development --open --hot",
    "build": "webpack --mode production",
    "check-types": "tsc"
  }
  1. Your folder structure should look something like this now:
  • my-react-app
  • app
    • node_modules
    • src
      • app.tsx
      • Index.tsx
      • Index.html
      • index.css
  • package.json
  • webpack.config.js
  • .babelrc
  • .gitignore
  1. Run the app:

In  your terminal, type: npm start

This should start the compile process in your terminal and open your browser window automatically and load the following url: http://localhost:8080

And you should be able to see “Hello World! in your browser.

Make a change to your app.js and you should be able to see the browser reload with new contents, this is because of Webpack’s Hot Module Replacement feature which gets enabled when we add --hot flag to npm start with webpack.

This is just a basic boilerplate.  As and when your app grows – you can configure additional dependencies and configurations through webpack and babel.

A few handy webpack plugins:

  • mini-css-extract-plugin: This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS.
  • clean-webpack-plugin: By default, this plugin will remove all files inside webpack’s output.path directory, as well as all unused webpack assets after every successful rebuild.
  • optimize-css-assets-webpack-plugin: It will search for CSS assets during the Webpack build and will optimize \ minimize the CSS
  • webpack-bundle-analyzer plugin: Visualize size of webpack output files with an interactive zoomable treemap.

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like