Author avatar

Kamran Ayub

Getting Started with React and TypeScript

Kamran Ayub

  • Feb 21, 2019
  • 15 Min read
  • 27,607 Views
  • Feb 21, 2019
  • 15 Min read
  • 27,607 Views
React
Typescript

Introduction

This guide provides an overview and introduction to building React apps with TypeScript using various approaches.

This guide assumes you are familiar with creating simple TypeScript projects using the npm tool and installing npm packages. You should also be familiar with React development and the supporting ecosystem as they'll be referenced throughout the guide.

For example, to create a new folder for each of the examples you can install Node.js and run these commands:

1
2
3
4
5
6
7
8
9
10
11
12
# ensure node is installed
node -v
npm -v

# create a new folder
mkdir my-typescript-app

# change directory
cd my-typescript-app

# initialize an empty package.json file
npm init
bash

The npm init command creates a new package.json file to store your project's dependencies installed via npm.

The other common command used throughout the guide is the npx command, which is a short way to execute a npm package without installing it in every project.

Getting Started Using Create React App (CRA)

The fastest way to get started using TypeScript with React is using Facebook's own Create React App (CRA) site generator. This will generate a fully-functional React app with many common features built-in like Babel, Webpack, Jest, and a hot reloading development server.

First, use the create-react-app npm package to generate a new TypeScript project my-typescript-app:

1
2
3
4
5
# Generate a new React Typescript project
npx create-react-app my-typescript-app --typescript

# wait for installation to be done!
cd my-typescript-app
bash

This will create a new folder my-typescript-app that will be your new project. It may take a few minutes while npm installs are the required dependencies.

Now you can start using TypeScript out-of-the-box by simply renaming your .js/.jsx files to be .ts/.tsx:

1
2
3
4
5
# Rename js to ts
mv src/index.js src/index.ts

# Start development server and watch for changes
npm start
bash

As you modify your project files the app will hot reload, immediately reflecting new changes. For information on how to add TypeScript an existing React project, see the CRA guide to adding TypeScript.

Type Checking Tests

One issue that has currently not been resolved is ensuring your tests are type checked. CRA will run TypeScript-based tests but will not show any TypeScript compiler errors, resulting in a mismatch between your development editor and the output of the CRA test task. It also means TypeScript errors in your tests will not break continuous integration builds.

To ensure tests are type checked, you can add an additional npm script:

1
2
3
4
5
{
    "scripts": {
        "test:tsc": "tsc -p tsconfig.test.json -w"
    }
}
js

This will run the TypeScript compiler directly in "watch" mode. The -p parameter references a new config you'll create, tsconfig.test.json in the root of the project:

1
2
3
4
5
6
7
8
{
    "extends": "./tsconfig.json",
    "include": [
        "src/**/*.ts",
        "src/**/*.test.ts",
        "src/**/*.test.tsx",
    ],
}
js

We are extending the default tsconfig.json and explicitly including test files which are excluded by default when CRA builds a project.

Now you can run this new npm script while you work on tests:

1
npm run test:tsc
bash

If your terminal or command prompt supports split outputs, that makes it easier to follow both commands.

Adding type checking support to your tests completes the TypeScript set up for CRA and allows a fully functional TypeScript experience developing React applications.

Getting Started from Scratch

While CRA provides an out-of-the-box experience for working with React and TypeScript with common React tooling, sometimes it's necessary to start from scratch. If CRA could be considered the “all-in-one” toolbox than you can think of this approach as starting with a hammer and a nail, simple but effective. The other approaches in this guide build on this by adding additional tooling, abstractions, and niceties that starting from scratch will offer.

To create a new TypeScript project with React support, start by installing all the required dependencies via npm:

1
npm install react react-dom typescript @types/react @types/react-dom --save
bash

Once the packages are installed, you'll need to configure a new TypeScript tsconfig.json file with specific support for JSX and React:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "compilerOptions": {
    // No module system, concatenated file
    "module": "none",

    // Set React as the JSX factory
    "jsx": "react",

    // Resolve modules using Node-resolution algorithm
    "moduleResolution": "node",

    // Target ES6 for more modern browsers
    "target": "es6",

    // Include typings from built-in lib declarations
    "lib": ["es2015", "dom", "dom.iterable"],

    // Include module source maps for debugging
    "sourceMap": true,

    // Output to single concatenated file
    "outFile": "dist/bundle.js"
  },
  "include": ["src"]
}
json

This sets up a basic project configuration to support React and React DOM. Any TS or TSX files in src will be compiled.

The important thing to call out here is that this does not use a module system or module loader. This concatenates the output of the files into one so there is no support for import or export syntax. Instead, you must use triple-slash directives to reference other TypeScript files and global dependencies must be included in the HTML page.

Add a new src/index.tsx file:

1
2
3
/// <reference path="App.tsx" />

ReactDOM.render(<App />, document.getElementById("root"));
typescript

This will set up rendering of our React app using the <App /> component in src/App.tsx:

1
const App = () => <div>Hello React App from scratch</div>;
typescript

We can now add some new npm scripts to build and watch the TypeScript to the package.json file:

1
2
3
4
5
6
{
  "scripts": {
    "build": "tsc -p .",
    "watch": "tsc -p . -w"
  }
}
json

Run the following command to build your app:

1
npm run build
bash

This will generate the dist/bundle.js file which contains the concatenated code.

To view our application, we will need to create an index.html file in the project root with references to React and React DOM as browser packages, then reference our compiled bundle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
  <head>
    <title>React and TypeScript</title>
  </head>
  <body>
    <div id="root"></div>

    <!-- Dependencies -->
    <script src="https://unpkg.com/[email protected]/umd/react.development.js" type="text/javascript"></script>
    <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js" type="text/javascript"></script>

    <!-- Compiled TypeScript -->
    <script src="dist/bundle.js" type="text/javascript"></script>
  </body>
</html>
html

We create our root element that React will render within and add a <script> references to the compiled bundle and other CDN package resources.

You can now preview the application using the serve npm package:

1
npx serve

Navigate to the URL displayed in the terminal to view the basic application.

Using triple-slash references works for small TypeScript projects but typically for web applications, there are a variety of assets that should be bundled like CSS, images, and third-party packages. This is when you'll likely want to introduce a module bundler and optimization tool.

Bundling Using Webpack

Webpack is a popular module bundler and optimizer. It can load many different types of code, understand their dependencies, and create a bundle that references everything in one fell swoop.

In the same project we created from scratch, add webpack and a special TypeScript module loader: ts-loader.

1
npm install webpack-cli webpack ts-loader --save-dev
bash

This example will use the simple usage of ts-loader but the documentation outlines more advanced workflows that you might choose to use.

Start by creating a new webpack.config.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
"use strict";

const path = require("path");

module.exports = {
  // Set debugging source maps to be "inline" for
  // simplicity and ease of use
  devtool: "inline-source-map",

  // The application entry point
  entry: "./src/index.tsx",

  // Where to compile the bundle
  // By default the output directory is `dist`
  output: {
    filename: "bundle.js"
  },

  // Supported file loaders
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "ts-loader"
      }
    ]
  },

  // File extensions to support resolving
  resolve: {
    extensions: [".ts", ".tsx", ".js"]
  }
};
js

This is a basic Webpack config that sets up our entry point (src/index.tsx) which Webpack will use to build the dependency tree and read our import statements. The module section describes different "module" (or file) types for Webpack to parse and load. By default, only JavaScript is supported but by installing ts-loader we can add TypeScript file support.

Since ts-loader takes care of handling most of our TypeScript config, we can simplify the tsconfig.json file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "compilerOptions": {
    // Allow importing like `import React from 'react'`
    "allowSyntheticDefaultImports": true,

    // Use ES2015 module system
    "module": "es2015",
-
-   // Resolve modules using Node-resolution algorithm
-   "moduleResolution": "node",

    // Set React as the JSX factory
    "jsx": "react",

    // Include typings from built-in lib declarations
    "lib": ["es2015", "dom", "dom.iterable"],

    // Include module source maps for debugging
    "sourceMap": true,
-    
-   // Output to single concatenated file
-   "outFile": "dist/bundle.js"
  },
- "include": ["src"]
}
diff

We can remove the outDir, moduleResolution, and include options since those are now handled by Webpack. The output will be still under the dist folder as that is the default folder Webpack uses.

Now replace the npm scripts with webpack equivalents:

1
2
3
4
5
6
7
8
{
  "scripts": {
-   "build": "tsc -p .",
-   "watch": "tsc -p . -w"
+   "build": "webpack"
+   "watch": "webpack -w",
  },
}
diff

Once the build command is run, a new dist/bundle.js will appear after Webpack compiles the application.

In order to view the application, we will need to create an HTML file to reference the bundle and render a page.

Add or modify the index.html file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
  <head>
-   <title>React and TypeScript</title>
+   <title>Webpack with React and TypeScript</title>
  </head>
  <body>
    <div id="root"></div>
-   
-   <!-- Dependencies -->
-   <script src="https://unpkg.com/[email protected]/umd/react.development.js" type="text/javascript"></script>
-   <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js" type="text/javascript"></script>

    <!-- Compiled TypeScript -->
    <script src="dist/bundle.js" type="text/javascript"></script>
  </body>
</html>
diff

We create our root element that React will render within and add a <script> reference to the compiled bundle Webpack produces.

You can now preview the application using the serve npm package:

1
npx serve

Navigate to the URL displayed in the terminal to view the Webpack-based example.

There were more than a handful of steps to configure TypeScript with React and Webpack from scratch but it's important to see what you're getting when using more sophisticated starters like CRA.

Webpack provides a wide array of features and offers huge flexibility for defining how your project is compiled. However, as seen above, it is not an "out-of-the-box" experience to configure it with TypeScript.

Bundling Using Parcel

An alternative to Webpack is the zero-configuration bundler, Parcel. Parcel offers an opinionated yet simple approach to module bundling that does not require extra configuration.

Starting from the previous "from scratch" TypeScript and React example, add Parcel to the project's dependencies:

1
npm install parcel-bundler
bash

Next, change the index.html file to represent Parcel's "entry point" into our application. Parcel will resolve dependencies by reading the <script> references in the file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html>
  <head>
-   <title>React and TypeScript</title>
+   <title>Parcel.js with React and TypeScript</title>
  </head>
  <body>
    <div id="root"></div>
-   
-   <!-- Dependencies -->
-   <script src="https://unpkg.com/[email protected]/umd/react.development.js" type="text/javascript"></script>
-   <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js" type="text/javascript"></script>

    <!-- Compiled TypeScript -->
-   <script src="dist/bundle.js" type="text/javascript"></script>
+   <script src="src/index.tsx" type="text/javascript"></script>
  </body>
</html>
diff

You can replace the npm scripts section now with a single start command:

1
2
3
4
5
6
7
{
  "scripts": {
+    "start": "parcel index.html",
-    "build": "tsc -p ."
-    "watch": "tsc -p . -w",
  },
}
diff

Simply run npm start now and Parcel will serve the application on port 1234 as well as automatically watch for changes and hot reload the application.

Parcel is a simple and easy way to get started with no extra configuration and can be customized further for more options.

Resources

This guide does not cover adding tests using Jest with TypeScript since CRA includes support by default. The other approaches in this guide require extra configuration and can be added using the ts-jest package.

All the example source code is available on GitHub.

Summary

Create React App provides the most comprehensive zero-configuration experience for working with production-ready TypeScript and React applications. On the other hand, when starting from scratch, it's possible to use React and TypeScript together without any additional build tools but it typically requires configuring a module loader to support more sophisticated dependencies.

Adding a tool like Webpack or Parcel provides the dependency and bundling support necessary to support larger projects. These tools offer ways to customize how the code is loaded and parsed while also providing useful developer experience features like hot reloading.

If you're still learning about using TypeScript with React, you can read the other Pluralsight guides on How to Statically Type React Components with TypeScript and Composing React Components with TypeScript.

46