The feeling, when you react, script and pack.

React with Typescript and Webpack

Saurabh Pati
HackerNoon.com
Published in
8 min readMay 29, 2018

--

Why am I writing this

At the time of writing this article, the popularity of React has already shot off the roof, that of Typescript is on the rise and it is safe to say that Webpack is the most preferred modern module bundler for an application. However, there is still an underlying dearth of a good example of the best possible way to start a project in React with Typescript and Webpack. Having said that, there are good resources out there if you want to use create-react-app cli or any other react starter kit out there. But there are very few ones if you want to have control over the configurations of your application.

The downside of using a cli like create-react-app are that you have to rely on the following tools the same way create-react-app relies on

  • Webpack
  • Babel
  • PostCSS w/ Autoprefixer
  • Jest
  • Flow (optional)

If you strongly dislike any one of the above, you are going to have a hard time accepting it. create-react-app lives upto its promise of ‘no build configuration’ because you simply cannot configure this tool. It provides a way of ejecting the tooling where you can let go of create-react-app if you want to extend the configuration further but why should I? Wouldn’t it be much more easier if I would have handled the configuration from the beginning in the first place? Moreover, once you eject, there is going to be an increase in the dependencies of your project, half of which you might not even need. Having said all that, it is probably the best react toolkit out there to get your project started if you are looking for one as a beginner or for a small to medium level project.

You may find this article helpful if you want to set up a project with the following things:

  • React — The primary front-end framework
  • Typescript — The primary language that compiles to javascript
  • Webpack — The application bundler.
  • Express — The server which will host the react application
  • Hot Module Reload — Something which will serve and load all your code changes you make while the server is running to the browser without you having to stop and run your app again.

Let the reacting, scripting and packing begin!

Before we begin, make an empty folder somewhere in your drive to begin afresh. If you are using a windows machine, go to your command line terminal and type the following three commands.

mkdir react-ts-webpack
cd react-ts-webpack
code .

the last command will open the recently created empty folder as a project in Visual Studio Code, a fairly popular editor and you are most likely to have it. Feel free to open it in your favorite editor if you do not have it.

So, as per popular tradition when you create a new project, initialize the npm in our project by typing the following line in the terminal.

npm init -y

Now we are going to create a few files and folders that are required to set up the project. So, on the root of your folder create the following files/folders.

  • Create file webpack.config.js: This is for webpack configurations
  • Create file tsconfig.json: This is for typescript configurations
  • Create file server.js: This is your server to start up the application
  • Create folder src.
  • Create folder app inside src.
  • Create a file index.html inside app and folder components
  • Create files App.tsx inside app and Hello.tsx inside components

So the folder structure of the application looks something like this

|-src
|-app
|-components
|-Hello.tsx
|-App.tsx
|-index.html
|-package.json
|-package-lock.json
|-server.js
|-tsconfig.json
|-webpack.config.js

Let the downloading begin!

Now we are going to download our production and development dependencies.

Download production dependencies

npm i react react-dom express typescript --save

Download development dependencies

npm i @types/react @types/react-dom webpack webpack-cli ts-loader webpack-dev-middleware webpack-hot-middleware html-webpack-plugin source-map-loader -D

Congratulations! we are almost halfway there without writing a single piece of code.

Let’s Code!

So, here are the contents of the files you just created. You can paste them onto your relative project from here (gist link is embedded in the title of the file) or type along.

index.html
tsconfig.json
webpack.config.js

Let’s react a bit!

Let us write a typical react component, after all this is the code that you will and should be concentrating on after you have completed the set up.

So, either you can write along or paste the code as you like.

Hello.tsx:

import * as React from 'react';interface IProps {
compiler: string,
framework: string,
bundler: string
}
export class Hello extends React.Component<IProps, {}> {
render() {
return <h1>This is a {this.props.framework} application using {this.props.compiler} with {this.props.bundler}</h1>
}
}

App.tsx:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Hello } from './components/Hello';
ReactDOM.render(<Hello compiler="Typescript" framework="React" bundler="Webpack" />,document.getElementById('root'));

Let’s get packing!

In case you are not familiar with webpack, a brief about the options is provided below. Feel free to skip it in case you are already aware of this stuff.

  • entry: The way webpack works is you provide it an entry and it claws its way up to build your app looking into what your entry imports and creates a dependency graph accordingly, the more properties you provide to this object, the same amount of bundles it is going to create, so by looking into the entry and output config, you can guess that it is going to create app.bundles.js and vendor.bundles.js. The reason for having separate bundles is because your react and react-dom code is unlikely to be changed, so having them as separate bundle (called vendor) makes sense for your browser to be able to cache it for performance. The webpack-hot-middleware/client in the app property is present for enabling the app code to be live reloaded.
  • output: Webpack looks at this configuration when it has to emit the bundled code from your app to the disk. path is the output directory for the code to be written and filename is as the name suggests the name of the file to be given to the output code.
  • devtool: Webpack looks at this config to add certain tools for development. Here source-map is added so that the code is source-mapped in the browser so that debugging can be made easy in development time.
  • resolve: Webpack looks here to decide whether to consider this file for bundling or leave it, so in our app it considers files with extensions ‘js’, ‘jsx’, ‘json’, ‘ts’, ‘tsx’ for bundling.
  • module: This configuration enables webpack to load a particular file when requested by the app with the help of loaders. In our app we are using ts-loader and source-map-loader. source-map-loader is already covered above. So, without ts-loader, webpack would not be able to understand this import in the App.tsx file since Hello component is a ‘tsx’ file understood by your editor but not by webpack when the import actually occurs.
import { Hello } from './components/Hello';
  • plugins: Webpack cannot do everything and it is kind of wrong for us to expect it to do everything. So, it overcomes its limitation by providing plugins to let it be extended beyond its capabilities like the html-webpack-plugin creates a template file to be served to the browser from our index.html file in the src folder and the HotModuleReplacement plugin enables our code to be hot reloaded removing the need to stop the server and run it again every time a change is made to the app.

Let’s Build!

You can now actually manually build your application. We will get to building automatically before running our server later. but for now if you want to take a look at your efforts until now, just add a ‘build’ script to your package.json file and execute the webpack command.

package.json:

...
scripts: {
...
"build": "./node_modules/.bin/webpack",
...
}
...

If you go to the terminal and run this command, you can look at your bundled application in the dist folder.

Run this in your terminal:

npm run build

Output in the terminal:

npm run build
bundled code

Let’s Express!

If you do not want to use express to run a server for you react application to be served, you can use webpack-dev-server which takes care of it and you do not have to write a server yourself but if you want to have the flexibility of a custom server for you to write middlewares, handle routes and modify requests and responses then it is recommended that you write your own server. At the time of writing this article, webpack-dev-server has no new features planned and is in maintenance only mode.

https://www.npmjs.com/package/webpack-dev-server

server.js:

const path = require('path'),
express = require('express'),
webpack = require('webpack'),
webpackConfig = require('./webpack.config.js'),
app = express(),
port = process.env.PORT || 3000;
app.listen(port, () => { console.log(`App is listening on port ${port}`) });app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, 'dist', 'index.html'));
});
let compiler = webpack(webpackConfig);
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true, publicPath: webpackConfig.output.publicPath, stats: { colors: true }
}));
app.use(require('webpack-hot-middleware')(compiler));
app.use(express.static(path.resolve(__dirname, 'dist')));

Now you can go to your package.json and add the start script

"start": "npm run build && node server.js"

Let’s reload!

Once you run the server by running ‘npm start’ in the terminal, the webpack-dev-middleware and webpack-hot-middleware should take care of hot module replacement but that is not the case.

You may notice that after you make a change, webpack duly compiles and emits the changed code but it only takes effect when you refresh your browser. That is not what we signed up for. The browser should have refreshed after re-building the changed component.

HMR in action

In order to fix this, we need to change our ‘App.tsx’ a bit

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Hello } from './components/Hello';
declare let module: any
ReactDOM.render(<Hello compiler="Typescript" framework="React..." bundler="Webpack" />,
document.getElementById('root'));
if (module.hot) {
module.hot.accept();
}

So basically we are saying our app to accept the hot reloaded changes made by webpack. The declaration part is needed because ‘hot’ property is not present by default on the ‘module’ object and we need to ask typescript to allow it.

Once you restart your server after this change, it works as expected.

HMR Fixed

I will try to add code splitting to further increase the efficiency of the build process and will be happy to receive any feedback regarding this post.

--

--