React 17 with Typescript starter kit without create-react-app (incl. Webpack, ESLint & Prettier) ⚛


*Recently updated*

There are many advantages of using create-react-app. With just few clicks you are ready to go, but some doesn’t want all that magic and tons of boilerplate code (me included). With CRA approach you’ll end up with massive “blackbox” which you will have to eject in most of the times.

If you are looking for clean and simple React + Typescript + Webpack (including dev server) + ESLint and Prettier starter kit, you’ve just found the right one. Follow the steps below or jump straight to the repository at:

Use this repo as a template:

If you find that helpful you can star the repository.

yarn init --yesornpm init --yes
yarn add typescript -Dornpm i typescript --save-dev
"compilerOptions": {
"sourceMap": true,
"noImplicitAny": false,
"module": "commonjs",
"target": "es5",
"lib": [
"removeComments": true,
"allowSyntheticDefaultImports": true,
"jsx": "react",
"allowJs": true,
"baseUrl": "./",
"esModuleInterop": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"downlevelIteration": true,
"paths": {
"components/*": [
"include": [
yarn add react react-dom
yarn add @types/react @types/react-dom -D
ornpm i react react-dom --save
npm i @types/react @types/react-dom --save-dev


<!DOCTYPE html>
<html lang="en">
<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 TypeScript App</title>
<div id="root"></div>


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

import App from "./App";

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


import React from "react";
import HelloWorld from "components/HelloWorld";

const App = () => <HelloWorld />;

export default App;

As you can see, i’ve used path bind to components directory — this also needs to be sorted out in webpack’s config in order to work.


import React from "react";

const HelloWorld = () => (
<h1>Hello World</h1>

<hr />

<h3>Environmental variables:</h3>
process.env.PRODUCTION: <b>{process.env.PRODUCTION.toString()}</b>
process.env.NAME: <b>{process.env.NAME}</b>
process.env.VERSION: <b>{process.env.VERSION}</b>

export default HelloWorld;

Adding Webpack

yarn add webpack webpack-cli webpack-dev-server ts-node @types/node @types/webpack @types/webpack-dev-server tsconfig-paths-webpack-plugin -Dornpm i webpack webpack-cli webpack-dev-server ts-node @types/node @types/webpack @types/webpack-dev-server tsconfig-paths-webpack-plugin --save-dev

We will run type checking through webpack (thanks to the great fork-ts-checker-webpack-plugin)

yarn add ts-loader fork-ts-checker-webpack-plugin html-webpack-plugin -Dornpm i ts-loader fork-ts-checker-webpack-plugin html-webpack-plugin --save-dev
import path from "path";
import webpack, {Configuration} from "webpack";
import HtmlWebpackPlugin from "html-webpack-plugin";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import {TsconfigPathsPlugin} from "tsconfig-paths-webpack-plugin";

const webpackConfig = (env): Configuration => ({
entry: "./src/index.tsx",
...(env.production || !env.development ? {} : {devtool: "eval-source-map"}),
resolve: {
extensions: [".ts", ".tsx", ".js"],
//TODO waiting on
plugins: [new TsconfigPathsPlugin()]
output: {
path: path.join(__dirname, "/dist"),
filename: "build.js"
module: {
rules: [
test: /\.tsx?$/,
loader: "ts-loader",
options: {
transpileOnly: true
exclude: /dist/
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html"
new webpack.DefinePlugin({
"process.env.PRODUCTION": env.production || !env.development,
"process.env.NAME": JSON.stringify(require("./package.json").name),
"process.env.VERSION": JSON.stringify(require("./package.json").version)
new ForkTsCheckerWebpackPlugin({
eslint: {
files: "./src/**/*.{ts,tsx,js,jsx}" // required - same as command `eslint ./src/**/*.{ts,tsx,js,jsx} --ext .ts,.tsx,.js,.jsx`

export default webpackConfig;

HtmlWebpackPlugin is used in order to simply point out to our index.html template. With webpack.DefinePlugin we can set and access environment variables inside our app (i.e check whether we are in dev or production mode, print out the version tag)

"scripts": {
"start:dev": "webpack-cli serve --mode=development --env development --open --hot",
"build": "webpack --mode=production --env production --progress",
"lint": "eslint './src/**/*.{ts,tsx}'",
"lint:fix": "eslint './src/**/*.{ts,tsx}' --fix"

Adding Prettier

yarn add prettier -Dornpm i prettier --save-dev
"printWidth": 100,
"trailingComma": "none",
"tabWidth": 4,
"semi": true,
"singleQuote": false,
"bracketSpacing": false,
"jsxBracketSameLine": false,
"arrowParens": "always",
"endOfLine": "auto",
"jsxSingleQuote": false,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"useTabs": false,
"htmlWhitespaceSensitivity": "css"

Also, I recommend setting up Prettier to run on each save. See:

Adding ESLint

yarn add eslint eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-prettier eslint-config-prettier eslint-plugin-import -Dornpm i eslint eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-prettier eslint-config-prettier eslint-plugin-import --save-dev
"parser": "@typescript-eslint/parser",
"plugins": [
"env": {
"browser": true
"extends": [
"parserOptions": {
"project": [
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-unused-vars": "off",
"react/jsx-filename-extension": [
"extensions": [
"react/prop-types": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
"settings": {
"react": {
"version": "detect"

Create .eslintignore file and add webpack config file to it


And voilà — we are ready to go. Start your app with start:dev script.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store