A Complete Guideline to Creating a Modern React App With TypeScript From Scratch

Mohammad Faisal - Jul 8 '23 - - Dev Community

To read more articles like this, visit my blog

Going inside any framework has two benefits. It makes you a better developer and improves your understanding of the job that you are doing.

It also helps you appreciate the things that these frameworks do under the hood that we take for granted.

I wanted to go deep inside React to see how an application really works under the hood by replicating it locally.

Today, I will take you with me on this journey. I will try to keep the concepts beginner-friendly for you. You don’t need to code with me. But if you do, that will be awesome!

Let’s begin.

UPDATE (November 27, 2021)

I have updated the article with the latest version of the webpack. which is currently on 5.64.4 and webpack-cli is on 4.9.1 .

Initialize a Project

First, create an empty folder anywhere on your machine.

mkdir react-from-scratch
Enter fullscreen mode Exit fullscreen mode

Initialize the Project With Yarn

Then initialize an npm project by running the following command (yarn recommended).

yarn init

or

npm init
Enter fullscreen mode Exit fullscreen mode

Here is what my output looks like with yarn.

yarn init command

Primary Setup

Then we will create two folders. src is to keep our project files, and build will be used to keep the bundle files (we get these after building the project).

mkdir src build
Enter fullscreen mode Exit fullscreen mode

Inside the src folder, create a index.html file. This is the only HTML file inside our project. This is why our application will be called a single-page application (SPA).

cd src
touch index.html
Enter fullscreen mode Exit fullscreen mode

The contents of our index.html are as follows:

<!DOCTYPE html>
<html lang="en">
  <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>React From Scratch</title>
  </head>

  <body>
      <div id="root"></div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

The only thing to notice is the div tag with the id="root" . This is the div that our application will connect later.

Install React

Okay, now we will install React and ReactDOM as a dependency because it will be very hard to create a React application without the React library itself :P

yarn add react react-dom
Enter fullscreen mode Exit fullscreen mode

You may ask what react-dom is doing here. Well, according to extensive research (i.e., a Google search):

“The react-dom package provides DOM-specific methods that can be used at the top level of your app and as an escape hatch to get outside of the React model if you need to” — ReactDOM documentation

That means react-dom acts as a middleman between DOM and React.

Create Index File

Now create a new file named index.tsx inside the src folder.

touch index.tsx
Enter fullscreen mode Exit fullscreen mode

Use the render method from the react-dom library to connect your index.html file’s content to your application.

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render( 
  <div> This is a new react app</div>, 
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

This render method takes two things as input:

  • A jsx component (for now, the normal div)

  • A real element from dom (our div with id="root” that we added previously in our index.html)

Pretty simple. But we haven’t added TypeScript yet. Let’s do that!

Install TypeScript

Let’s now set up our TypeScript dependencies because modern React applications should be built with TypeScript.

yarn add -D typescript [@types/react](http://twitter.com/types/react) [@types/react-dom](http://twitter.com/types/react-dom)
Enter fullscreen mode Exit fullscreen mode

Configure TypeScript

Now let’s create a new file named tsconfig.jsonin the projects root directory (outside src).

touch tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Add the following content:

{
    "compilerOptions": {
      "target": "ES5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
      "module": "ESNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
      "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ /* Type declaration files to be included in compilation. */,
      "lib": [
        "DOM",
        "ESNext"
      ] /* Specify library files to be included in the compilation. */,
      "jsx": "react-jsx" /* Specify JSX code generation: 'preserve', 'react-native', 'react' or 'react-jsx'. */,
      "noEmit": true /* Do not emit outputs. */,
      "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */,
      "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
      "strict": true /* Enable all strict type-checking options. */,
      "skipLibCheck": true /* Skip type checking of declaration files. */,
      "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
      "resolveJsonModule": true,
      "allowJs": true /* Allow javascript files to be compiled. Useful when migrating JS to TS */,
      "checkJs": true /* Report errors in .js files. Works in tandem with allowJs. */,
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules", "build"] // *** The files to not type check ***
  }
Enter fullscreen mode Exit fullscreen mode

Comments in this file explain what each of the options does. You may want to tweak little things here and there to satisfy your needs.

Install Babel

Now we need to install Babel into our project. That’s because our browsers don’t understand anything but JavaScript, so if we want our browsers to understand our jsx or tsx- files, we need some compiler.

Babel will do that for us. Let’s install some dependencies:

yarn add --dev \
  @babel/core \
  @babel/cli \
  @babel/preset-env \
  @babel/preset-typescript \
  @babel/preset-react
Enter fullscreen mode Exit fullscreen mode

Explanation

The first three libraries are needed by default. We need preset-typescript for compiling TypeScript and preset-react for compiling our React-specific files.

If we want additional support for async/awaits, we will need to add two additional libraries.

yarn add -D \
 @babel/runtime \
 @babel/plugin-transform-runtime
Enter fullscreen mode Exit fullscreen mode

These will help to compile async/await syntax, which is a must at this point.

Configure Babel

We will create a new file named .babelrc in the project's root folder.

touch .babelrc
Enter fullscreen mode Exit fullscreen mode

Add the following configuration there.

{
    "presets": [
        "@babel/preset-env",
        [
            "@babel/preset-react",
            {
                "runtime": "automatic"
            }
        ],
        "@babel/preset-typescript"
    ],
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "regenerator": true
            }
        ]
    ]
}
Enter fullscreen mode Exit fullscreen mode

Install webpack

To get webpack working, we will need to install some dependencies.

yarn add --dev \
    webpack \
    webpack-cli \
    webpack-dev-server \
    style-loader \
    css-loader \
    babel-loader \
    html-webpack-plugin \
    clean-webpack-plugin
Enter fullscreen mode Exit fullscreen mode

Configure webpack

Then create a new file named webpack.config.js in the root folder.

touch webpack.config.js
Enter fullscreen mode Exit fullscreen mode

Add the following content there.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    entry: path.resolve(__dirname, 'src', 'index.tsx'),
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
    },
    mode: 'development',
    module: {
        rules: [
            {
                test: /\.[jt]sx?$/,
                use: ['babel-loader'],
                exclude: /node_modules/,
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader'],
            },
            {
                test: /\.scss$/,
                use: ['style-loader', 'css-loader', 'sass-loader'],
            },
            {
                test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
                type: 'asset/resource',
            },
            {
                test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
                type: 'asset/inline',
            },
        ],
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js', '.jsx'],
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, './src/index.html'),
        }),
        new CleanWebpackPlugin(),
    ],
    devServer: {
        static: path.join(__dirname, './src'),
        port: 3001,
        hot: 'only',
        compress: true,
        open: true,
    },
};
Enter fullscreen mode Exit fullscreen mode

Quite confusing? Not really. If you are familiar with webpack, then this configuration should make sense.

If you want to learn more about the explanation of this setup, you can refer to the following article. We won't go into details here for now.

6 Webpack Concepts for Advanced React Developers
Concepts to help you understand React from the inside betterprogramming.pub

Add Script

Now go into the package.json file and add the following script for starting the application.

"scripts": {
  "start": "webpack serve --config webpack.config.js --env env=development",
  "build": "webpack --config webpack.config.js --env env=production"
},
Enter fullscreen mode Exit fullscreen mode

Open a terminal and run the following command:

yarn start
Enter fullscreen mode Exit fullscreen mode

You should be greeted with the application running on your browser.

App Running

Similarly, if you want to build for production, you can run

yarn build
Enter fullscreen mode Exit fullscreen mode

Congratulations! You have successfully set up a React application on your own. But if you are interested in good standards, here are bonus sections.

Install Prettier

Install the following dependencies for Prettier.

yarn add -D
  prettier \
  eslint-config-prettier \
  eslint-plugin-prettier
Enter fullscreen mode Exit fullscreen mode

Configure Prettier

Create a file named .prettierrc.js inside the root folder.

touch .prettierrc.js
Enter fullscreen mode Exit fullscreen mode

Add the following content:

module.exports = {
    semi: true,
    trailingComma: 'all',
    jsxSingleQuote: false,
    singleQuote: true,
    printWidth: 120,
    tabWidth: 4,
};
Enter fullscreen mode Exit fullscreen mode

Install ESLint

To install ESLint, first, install the following dependencies:

yarn add -D \
  eslint \
  eslint-plugin-react \
  eslint-plugin-react-hooks \
  @typescript-eslint/parser \
  @typescript-eslint/eslint-plugin \
  eslint-plugin-jsx-a11y \
  eslint-plugin-import \
Enter fullscreen mode Exit fullscreen mode

Configure ESLint

Then add a file named .eslintrc.js inside the root folder.

touch .eslintrc.js
Enter fullscreen mode Exit fullscreen mode

Add the following contents.

module.exports = {
    parser: '@typescript-eslint/parser',
    parserOptions: {
        ecmaVersion: 2020,
        sourceType: 'module',
        ecmaFeatures: {
            jsx: true, // Allows for the parsing of JSX
        },
    },
    settings: {
        react: {
            version: 'detect',
        },
    },
    extends: [
        'plugin:react/recommended',
        'plugin:react-hooks/recommended',
        'plugin:@typescript-eslint/recommended',
        'plugin:import/errors',
        'plugin:import/warnings',
        'plugin:import/typescript',
        'plugin:jsx-a11y/recommended',
        'plugin:prettier/recommended', //should be at the last
    ],
    rules: {
        'no-unused-vars': 'off',
        '@typescript-eslint/no-unused-vars': ['error'],
        '@typescript-eslint/no-var-requires': 'off',
        'react-hooks/rules-of-hooks': 'error',
        'react-hooks/exhaustive-deps': 'warn',
        'react/prop-types': 'off',
        'react/jsx-uses-react': 'off',
        'react/react-in-jsx-scope': 'off',
        '@typescript-eslint/explicit-module-boundary-types': 'off',
    },
};
Enter fullscreen mode Exit fullscreen mode

Script for Linter and Prettier

Now add two more scripts to run ****Prettier and ESlint.

 "scripts": {
    "start": "webpack serve --config webpack/webpack.config.js --env env=dev",
    "build": "webpack --config webpack/webpack.config.js --env env=prod",
    "lint": "eslint --fix \"./src/**/*.{js,jsx,ts,tsx,json}\"",
    "format": "prettier --write \"./src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\""
},
Enter fullscreen mode Exit fullscreen mode

That’s it. Now you have a fully functioning React application with TypeScript. Here is the repository.

Mohammad-Faisal/react-typescript-template-from-scratch
Contribute to Mohammad-Faisal/react-typescript-template-from-scratch development by creating an account on GitHub. github.com

Imagine how many things are being made easy for us by create-react-app. I hope you learned a thing or two from this article.

Have a great day! :D

Resources

Building a Modern React App From Scratch in 2021 by Yakko Majuri

Get in touch with me via LinkedIn or my Personal Website.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .