I have been doing some new projects in React with Typescript, and I wanted to dump here the configuration I had been using. Usually, that type of boilerplate can either be generated by the npx command or established via trial and error.

Since it can be frustrating and dull, I tend to save the configuration, so I can easily refer to it later on if needed. A template would be ideal, but it’s also time-consuming to maintain and keep up to date. At least here you should have enough to get started. I’ll appreciate any templates or related configurations you have found that are better than this one, please share it in the comments below. 👌

Source Configuration

From react-scripts

If you don’t want to manually set it up, you can use the create-react-app command to generate a new project with typescript. Use the npx command to create a new project with the following command:

npx create-react-app my-app --template typescript

This will set up the project using npm and create a new working React project with typescript in the my-app directory. Else you can follow the next part of the article to set it up manually. We will explain everything to do, to set up your project.

Setting up the project

Manually though, for the bare minimum, you will need the following dependencies. Though to actually run the UI you will need something like babel to transpile it or use the template’s react-scripts:

{
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },
  "devDependencies": {
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "typescript": "^5.6.2"
  }
}

Then in your tsconfig.json, it should be similar to any typescript project, minus the React specifics.

Use the following configuration, you may need to adjust it to your needs:

{
  "compilerOptions": {
    "baseUrl": ".",
    "target": "es5",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "jsx": "react",
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "allowJs": true,
    "noEmit": true,
    "isolatedModules": true
  },
  "include": [
    "**/*.ts",
    "**/*.tsx",
    "**/*.js"
  ],
  "exclude": [
    "node_modules"
  ]
}
  • Note thejsx set to react to enable JSX syntax (You might use react-jsx too).
  • We also import the dom and dom.iterable libraries to interact with the DOM since it’s a React project.
  • The allowSyntheticDefaultImports allows to import React modules as import React from 'react' instead of import * as React from 'react'.

Some of it might be redundant or not necessary in your case (e.g. you might not need to use resolveJsonModule to import JSON data in your app, but it’s useful to know), but it’s a working starting point that I have used. If you have a leaner or better one, let me know. 🤓

Using webpack with tsconfig.json

  • Besides webpack, you can also use other alternatives like vite.
  • If you are using a React frameworks like Next.js, it might have its own configuration, so you won’t have to set up by yourself.

If you are using webpack to bundle your project, and babel to transpile it, you will need to install these dependencies:

yarn add --dev html-webpack-plugin webpack webpack-cli webpack-dev-server
yarn add --dev @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript babel-loader

Now you can update the webpack.config.js to include the following configuration.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/index.tsx',
    mode: 'development',
    module: {
        rules: [
            {
                test: /\.(ts|tsx)$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'],
                    },
                },
            },
        ],
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js'],
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist'),
    },
    devServer: {
        static: {
            directory: path.join(__dirname, 'dist'),
        },
        compress: true,
        port: 9000,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
        }),
    ],
};

And now assuming the project is properly set up with a index.html and a React index.tsx file, you can add commands to your package.json to run the project:

{
  "main": "index.js",
  "scripts": {
    "start": "webpack serve --open",
    "build": "webpack"
  }
}

There are other alternatives to webpack and babel, but I have used this setup in the past and it works well. If you know better, I am keen to be enlightened 💡 on the subject.

Using custom paths

You can set up custom paths in your tsconfig.json to make it “easier” to import your files. Though when changing the file’s directory, my IDE didn’t always update all the path accordingly.

I’ll leave the setup here as it is a nice to know feature. First you need to set up the paths in the tsconfig.json:

{
  "compilerOptions": {
    "paths": {
      "@_components/*": [
        "src/components/*"
      ],
      "@_hooks/*": [
        "src/hooks/*"
      ]
    }
  }
}

Then make sure you do the same for your webpack.config.js, if you are using it (following the previous example). In my case, I am creating two paths for the components and hooks:

module.exports = {
    // ... other configs
    resolve: {
        extensions: ['.tsx', '.ts', '.js'],
        alias: {
            '@_hooks': path.resolve(__dirname, 'src/hooks'),
            '@_components': path.resolve(__dirname, 'src/components'),
        }
    },
};

Nice! There’s a new alias for the components and hooks, everything following that path (src/components and src/hooks) can be imported with the alias (e.g. @_components and @_hooks). It also works with the subdirectories.

And for example, I can easily import my hook using:

import useMyHook from '@_hooks/useMyHook';

And that’s it! 🎉 The hook import path will be recognised within the IDE and the project will compile correctly. When using multiple subdirectories, the import path may become very long, so having this shorter path alias can reduce the clutter in the import statement.

Test configuration

Test setup in typescript

We have talked about configuring jest for a typescript project in a previous article, here it will be specifically for a React project, with the necessary dependencies and configuration.

First let’s install the dependencies:

yarn add --dev @testing-library/dom @testing-library/jest-dom @testing-library/react @types/jest jest jest-environment-jsdom ts-jest typescript

Now your package.json should include those dependencies as well as the one previously installed in the source configuration parts. But those are the main ones needed for your test setup:

{
  "devDependencies": {
    "@testing-library/dom": "^10.4.0",
    "@testing-library/jest-dom": "^6.5.0",
    "@testing-library/react": "^16.0.1",
    "@types/jest": "^29.5.12",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "ts-jest": "^29.2.5",
    "typescript": "^5.6.2"
  }
}

Let’s continue for the jest configuration in the jest.config.js file, though, you can also have it as a typescript file, it’s rather easy to convert:

module.exports = {
    roots: ['<rootDir>/src'],
    transform: {
        '^.+\\.tsx?$': 'ts-jest',
    },
    moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
    testRegex: '\\.(test|spec)\\.tsx$',
    setupFilesAfterEnv: ['<rootDir>/setupTests.ts'],
    testPathIgnorePatterns: ['<rootDir>/.next/', '<rootDir>/node_modules/'],
    testEnvironment: 'jest-environment-jsdom',
    collectCoverage: true,
    coverageReporters: ['json', 'lcov', 'text', 'clover'],
    moduleDirectories: ['node_modules', '<rootDir>/'],
};

In this configuration:

  • the tests are expected within the src directory, so we use the roots option to specify that.
  • It will use ts-jest to transform the typescript test files and run them.
  • It will run the tests matching the testRegex ending with .test.tsx or .spec.tsx.
  • The setupTests.ts file will be used to set up the test environment before running the tests.
  • It will also use the jest-environment-jsdom to run the tests in a browser-like environment (to interact with the DOM).
  • It will also collect coverage automatically collectCoverage set to true and report it in different formats with the coverageReporters option.

And now you should be ready to write some tests for your React components in typescript. Check out these articles for some testing help about static and interactive testing in React.

Final touches

Missing eslint? You can set up eslint following this article. Eslint is useful to consolidate your code style and enforce some rules in your project. It is not mandatory, but I find using it helps me keep the code clean and consistent.

With the source and test configuration set up, nothing should stop you now from building the best React app you have ever made! Until the next one. 🙃