Setting up a React + TypeScript + SASS + Webpack and Babel project in 6 Steps#
In this tutorial I want to show you how to set up a bulletproof, IE 11 safe, frontend project using TypeScript, React and Sass.
Note: I will use yarn instead of npm in this tutorial. If you never heard of it, you should check it out! 😁
Initializing a blank project#
Let's start by creating an empty folder and initializing a blank node project.
mkdir awesome-appcd awesome-appyarn init
Which will result in the following file structure:
.└── package.json
Installing dependencies#
Before we can start to write some of the sweet react code, we first have to set up our transpiration environment. In this case with Webpack and Babel.
But why do I need to transpile my code?
Well, there are three reasons, why we need to transpile our code:
- Browsers don’t support TypeScript (at the moment), so we need to transpile it to JavaScript.
- Not all browsers support modern JavaScript (especially IE). We need to transpile it into an “older version”, so that common browsers can interpret it correctly.
- We need to transpile our SASS to CSS, because (you might guessed it already) nearly no browser supports SASS.
Let’s install all the dependencies we need:
yarn add core-js react react-dom regenerator-runtimeyarn add -D @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript @types/react @types/react-dom babel-loader css-loader node-sass sass-loader source-map-loader style-loader webpack webpack-cli typescript ts-loader
Event though it looks like a lot of dependencies, we only have 4 of them which are going to be built into our application, the rest are development dependencies that are only required by our development environment.
If you are interested, here is a short description of every package:
react // React frameworkreact-dom // React's DOM frameworkcore-js // Polyfills for a lot of ECMAScript methodsregenerator-runtime // Polyfill for runtime/* Webpack */webpackwebpack-cli/* Babel core and presets to transpile TypeScript */@babel/core@babel/preset-env@babel/preset-react@babel/preset-typescript/* Type Definitions for React */@types/react@types/react-dom babel-loader/* For transpiling SASS */style-loadercss-loadernode-sasssass-loader/* For better debugging */source-map-loader/* TypeScript core package and Webpack loader */ts-loadertypescript
Setting up Webpack#
Now that we got all our dependencies installed it is time to create our webpack.config.js
file.
Webpack will manage our loaders. Loaders are software components (npm packages) that will change/transpile/extract or analyze our code.
Our webpack.config.js
will look like this:
module.exports = {mode: "development",watch: true,entry: "./src/index.tsx",output: {filename: "bundle.js",path: __dirname + "/dist"},resolve: {extensions: [".ts", ".tsx", ".js", ".json"]},devtool: "source-map",module: {rules: [{ test: /\.scss$/, use: [ "style-loader", "css-loader", "sass-loader" ] },{ test: /\.tsx?$/, loader: "babel-loader" },{ test: /\.tsx?$/, loader: "ts-loader" },{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" }]}};
I think most of the options are self-explanatory, but I want to quickly talk about four of them.
watch
states that Webpack will automatically recompile if it noticed a file change aka. cmd + s.
devtool: "source-map"
will add source maps, which will make your developer life a lot easier, since they provide TypeScript sources to your browser devtools, so class names, interfaces and so on don’t get lost. Important: Remove this option and line 17 for a production build.
["style-loader", "css-loader", "sass"-loader"]
is a loader chain. Which basically means that the output (return
) from the right loader will be used as the input by the next loader and so on (right to left!
). This loader chain will extract SASS from the SASS files, transpile it to CSS and finally to JavaScript.
babel-loader
will transpile TypeScript to JavaScript (ES2015 in our case) based on the .babelrc
file which we will configure in the next step.
Transpiling is needed because browsers can’t understand TypeScript at the moment, in addition, we need to produce “old” JavaScript to get a good browser coverage.
Setting up Babel#
Setting up babel is a piece of cake! Babel will help us transpiling our TypeScript to the right JavaScript standard. This is our .babelrc
config file:
{"presets": ["@babel/react","@babel/typescript",["@babel/env",{"modules": false,"targets": {"chrome": "58","ie": "11"}}]],}
We basically tell Babel to use it’s React and Typescript presets and to transpile to a minimum version of IE 11 and Chrome 58.
Setting up TypeScript#
Hold tight, we are very close to writing our first line in TypeScript, but before we can do that, we need to create a file called tsconfig.json
. This file will hold (you might guessed it already) some TypeScript configurations:
{"compilerOptions": {"outDir": "dist/","noImplicitAny": true,"module": "commonjs","target": "es2015","jsx": "react"},"include": ["./src/**/*"]}
I also think that most of the options are clear, but I want to shortly explain two of them:
noImplicitAny
is a flag, which will force you to declare typed function arguments. Even though it is not required, I encourage you to use it, because otherwise you can get confused by not knowing which type your function argument was.
jsx
states that we want to write web component syntax in our JavaScript, or in our case TypeScript files.
Our project structure is now looking like this:
.├── .babelrc├── node_modules├── package.json├── tsconfig.json├── webpack.config.js└── yarn.lock
Let’s write some React!#
Congratulations, you made it! You can start writing React now 🥳
Since this article isn’t about writing React, I won’t go into details about my React code, but I want to show you how to use SASS with your React components.
Let’s create an index.html
in your project root:
index.html<!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>Awesome App</title></head><body><div id="app"></div><script src="dist/bundle.js"></script></body></html>
Now let’s add some Code!
src/components/Banner/Banner.scss@import "../../styles/colors";.banner {background-color: $moccasin;box-sizing: border-box;padding: 2rem;text-align: center;&__text {color: $dark-red;}}
src/components/Banner/Banner.tsximport * as React from "react";import "./Banner.scss";interface IProps {name: string;}export default class Banner extends React.Component<IProps> {public render() {return (<div className="banner"><span className="banner__text">Hello {this.props.name}!</span></div>);}}
src/index.tsximport "core-js";import "regenerator-runtime/runtime";import * as React from "react";import * as ReactDOM from "react-dom";import Banner from "./components/Banner/Banner";import "./styles/global.scss";ReactDOM.render(<div><Banner name="Max" /></div>,document.getElementById("app"),);
src/styles/colors.scss$moccasin: moccasin;$dark-red: darkRed;
src/styles/global.scsshtml {font-size: 10px;}body {font-size: 1.8rem;margin: 0 auto;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}
To make my life easier I added an Webpack alias to my package.json
..."scripts": {"webpack:dev": "npx webpack --config webpack.config.js"}...
Now you can run yarn run webpack:dev
and open the index.html
to see the working app. 🤩
I hope you found this tutorial helpful, If you want to take a deeper dive into the code, I will link you the GitHub repo right down below.
Cheers, Max.
You can find the GitHub repository for this article here.