For the last two years, I’ve worked a lot on small web projects where I used a simple TypeScript starter project that removes the basic installation process that a new project always has.
I wanted to create a new starter project and document the steps to provide some value to others and help them create their own.
Once this starter is finished, you can mark it as a template repository in GitHub. This simply means that you can easily use this as a basic setup whenever you create a new repository.
What will the starter include?
Here is the tech stack we’re going to use in this starter project:
- .editorconfig — to enforce consistency across different editors.
- Yarn — for installing external packages.
- Prettier — for automatic code formatting
- ESLint — for automatic code linting
- Babel — to transpile the code to something all browsers understand
- Webpack — for bundling your code
- PostCSS — so you can use modern CSS features today
- Cypress — for running End-to-End tests
PS: Each step is committed separately on the GitHub repository for this starter. If you get stuck while implementing it yourself, you can look through the commits to double-check your work.
Creating your GitHub repository
Log in to your GitHub profile and create a new repository. Give it a name like
ts-starter-project and make sure to check the boxes to add a
You can see the settings I used on the screenshot above.
Creating your folder
Navigate to your projects folder in the terminal, clone your repository, and navigate to the folder.
1. Adding a
Editorconfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs. The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable editors to read the file format and adhere to defined styles. EditorConfig files are easily readable, and they work nicely with version control systems.
.editorconfig file in the root of your project and paste in the following contents:
You can configure this to your liking, but I prefer to set my indentation to 4 spaces and always insert a final newline. These are very conventional standards and are good to follow.
2. Creating a simple Hello World page with WebPack and Babel
2.1 Setting up the basics
npm init -y in the terminal from within your project folder. This will create a default
package.json file for you.
2.2 Installing Yarn
If you don’t already have yarn installed, you can install it globally with the following command:
PS: It’s okay if you prefer to use NPM. Personally, I prefer Yarn.
2.3 Installing Babel
2.3 Setting up Webpack
For this project, we’re going to install the latest version of Webpack as a development dependency. Run the following command to add both Webpack and the Webpack Command Line Interface (
webpack-cli) packages into your package.json.file:
Create another file in the root of your project, name it
webpack.config.js, and paste in the following code:
This will look for all the JS assets in the
app directory and it will copy all the compiled assets into the
Now create the following files and folders:
Let’s place a simple alert in the
app.js, so we can easily see if the code is loaded correctly:
Now, finally, let’s add some HTML and see if our setup works. Paste the following content into the
If you open up the
Let’s add a build command to the package.json file. In the
scripts section, add the following line:
Now run the build command from the terminal:
You should get a message stating that Webpack has successfully compiled. If you refresh the browser window, you should be greeted with an alert now.
Don’t forget to add the public folder to the
.gitignore file. We don’t need to commit these files, which are generated on every build:
2.4 Loading the page dynamically
So far we have opened the
index.html file directly into the browser. The script file was added manually into the HTML file as well. This works for now, but as the app grows there will be more scripts and styles. Webpack will smartly split these into several files based on performance best practices.
These files shouldn’t be manually added to the HTML file. Webpack should take care of this for us.
There’s a package called
HTMLWebpackPlugin that handles this. It will create an
index.html file with all scripts/styles which are needed. Install the package with the following command:
Remove the hardcoded script reference from the
Add the following code to the
If you run the build command again, it should generate a new
index.html file inside the public folder which works exactly like the previous one, but this time with dynamically added assets.
2.5 Adding Webpack Dev Server
We’re now going to add Webpack Dev Server, which comes with several advantages:
- It starts up a development server, so we no longer have to manually load the index.html in the browser, instead, we’ll receive a localhost link.
- It provides live reloading. When assets are modified, it will automatically refresh the contents of the page.
Install the package with the following command:
Add the following command to the scripts section in your package.json:
If you run this command (
yarn serve), you should get the following output:
Ctrl or Cmd + click the link to open it in your browser, where you’ll see the app running.
3. Add TypeScript
Let’s change the name of the
app.js file to
app.ts. Also in the
webpack.config.js we should change the reference to reflect the filename change.
Moreover, let’s add some actual TypeScript code into our
If you run the build command again, you’ll be greeted with the following error:
To fix this, we need to do several things:
- Install TypeScript and set up a
tsconfig.jsonfile to set up a TypeScript configuration
- Install a Webpack loader so that Webpack knows how to handle TypeScript files
- Use the loader in our
Run the following command in your terminal:
Now create a
tsconfig.json file in the root of your project and paste in the following code:
This will configure your project to have strict mode enabled. Meaning, TypeScript will have stricter rules for your code. Since we have no prior code that needs to be refactored, this will ensure the quality of future code with no effort needed at this time.
Now let’s install the TypeScript loader:
We can now use the
ts-loader in the Webpack config file to handle TypeScript files with the following code:
If you restart your Webpack dev server, you will now be greeted by an alert message proving that your TypeScript file was handled correctly.
Adding Prettier and ESLint
Prettier is an opinionated code formatter that reprints your code on save so that you don’t have to worry about the proper way to handle line-breaks, empty lines, extra spaces, or the right type of quotes your team uses.
Prettier has a nice explanation of the differences here.
Run the following commands to install Prettier and create the configuration files:
.prettierignore file will hold a list of all the files we don’t want Prettier to format, this can stay in sync with the
.gitignore in many ways. Add the
public folder to this file to start with.
.prettierrc.json file contains any changes to the configuration options we want to make. Here’s the configuration I prefer to use in my projects:
This removes trailing commas, forces semicolons, changes double-quotes to single-quotes, uses no quotes in object-properties, or all properties will be in quotes and stay at a maximum of around a 100 character line length before breaking the code to several lines.
Formatting existing code
Now run a one-time command to let Prettier format all of our existing code.
If you’ve followed along with the tutorial you’ll see Prettier made changes to the
README.md and even the
To integrate ESLint, we need to install several packages. I’ll explain the use of each of them briefly:
- eslint — For actually using ESLint
- @typescript-eslint/eslint-plugin — Adding TypeScript linting rules to ESLint
- @typescript-eslint/parser — Allow ESLint to parse TypeScript code
- eslint-watch — So we can run ESLint on file changes
- eslint-config-airbnb-base — AirBNB has a fantastic set of rules available that give us a great starting point of rules to follow
- eslint-plugin-import — This adds ESLint support for linting import/export syntax and prevents issues with misspelling of file paths and import names
- eslint-config-prettier — Turns off all rules that are unnecessary or might conflict with Prettier
- eslint-plugin-prettier — Runs Prettier as an ESLint rule and reports differences as individual ESLint issues
Run the following command to install all of these:
Now create a
.eslintrc.json file with the following command:
Here we’re going to add our config for ESLint, which rules we want to follow, which plug-ins we want to use etc. Add the following code to the file:
Now let’s add two more scripts to our package.json. One to run ESLint once and report any errors, and one to watch file changes and report issues as they arise:
Now if you run either of these commands you should get two errors in the
app.ts file. You don’t want to trivially set a variable to a string type if TypeScript can 100% infer this type from usage, and you don’t want to accidentally ship alerts to production.
To fix this, let’s empty the
app.ts for the time being.
We’re going to install PostCSS together with some PostCSS plug-ins that will help with styling:
- postcss-preset-env — This lets you convert modern CSS into something most browsers can understand, determining the polyfills you need based on your targeted browsers or runtime environments
- postcss-nested — This allows a Sass-like unwrapping of nested rules. See the GitHub repo for more information.
- postcss-import — This plugin transforms @import rules by inlining content
- postcss-custom-properties — This plugin lets you use CSS Custom Properties in PostCSS
- cssnano — cssnano is a modern, modular compression tool written on top of the PostCSS ecosystem, which allows us to use many powerful features to compact CSS appropriately.
- postcss-loader — A loader to handle PostCSS files
- css-loader — The css-loader interprets @import and url() like import/require() and will resolve them.
- style-loader — A loader to handle adding styles into the DOM
- mini-css-extract-plugin — This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.
Let’s install all of them, including
Let’s create a PostCSS configuration file:
Paste the following config into the file:
The existence of this file in the root of your project will automatically tell PostCSS to use these plug-ins.
To get everything to work, you should add the loaders to the
Finally, let’s create a CSS file with some styling in it to verify that everything works as expected. Create a
main.pcss file in the
app/css/ folder and add it as an entry-point to Webpack. If all works well, your page should now have a very noticeable background.
PS: If you feel you don’t need some PostCSS plug-ins, feel free to leave them out of the yarn install and
In order to add Jest to our TypeScript project, we’re going to have to install three packages:
- Jest — Obviously, we need Jest itself.
- ts-jest — This is a Jest transformer with source map support that lets you use Jest to test projects written in TypeScript.
- @types/jest — This package contains the Type definitions for Jest.
Let’s install all three of them to our devDependencies:
Furthermore, let’s update our test command in the package.json to run Jest right away. Moreover, let’s add a watch command, so we can write tests that run as we save files.
ts-jest provides a command for generating a jest.config.js file with
ts-jest set as the parser. Let’s run this now to create the file.
Finally, we need to let TypeScript know about the Jest type definitions. Update the types array in the
We can now add our first test. Let’s create a simple function that we can test in the
Now create an
app.test.ts in the same folder, which will contain our tests for the
This file simply imports the function from
app.ts and tests that 1 and 2 add up to 3.
If you run the test command in your terminal now, you should see a passing test.
Bonus Jest Tip
If your editor has support for a Jest extension, this helps with running tests in the background automatically. I use VSCode and have installed the Jest extension. With the following setting in my VSCode settings, it now automatically runs tests when the test- or source files changes.
Cypress is a next-generation front-end testing tool built for the modern web. It allows you to run tests against your application from a user standpoint and validate that the user gets the experience you meant for them.
Run the following command to install Cypress:
Add the following line to your package.json scripts, so you can easily open Cypress:
If you run the command, you’ll see that Cypress will install some demo tests against an application they have online. The demo tests are incredibly helpful to see what Cypress can do and how to do it. I’d recommend you go through them and play around with Cypress yourself based on these tests.
When you’re done with that, you can delete all files in the
I’d also recommend adding the
cypress/screenshots folder to your
We’ve come to the end of this tutorial, and it’s time for some clean-up, so your new projects won’t be created with demo code. You can empty the following files:
I hope you learned something from this tutorial on how to build a simple TypeScript starter. If you have any thoughts, questions, feedback, or improvements, please let me know in the comments.
The GitHub repository contains separate commits for all chapters in this article, in case you want to go through the changes there.