How To Style Your Next.JS Projects EXACTLY Like Google (in TypeScript)

How To Style Your Next.JS Projects EXACTLY Like Google (in TypeScript)

Learn how to utilize TypeScript, ESLint, Prettier, Husky & VS Code to style a Next.JS Project using Google's TypeScript Style Guide.

ยท

10 min read

Featured on Hashnode

Introduction

Sticking to a consistent style guide is something that we all love doing as programmers. Whether it's spaces instead of tabs, double quotes instead of single, or putting your {'s on a new line (if you are a savage).

Sometimes, however, we can slip up and let small differences in our styling exist throughout our codebase. The problem arises when codebases become bigger and bigger over time, and small inconsistencies sneakily add up over the years.

In this guide I'm going to run through how you can:

  • Setup your Next.JS projects using Typescript, ESLint, Prettier, and Husky.
  • Lint and format your code to align to the style rules you defined in config.
  • Auto-format and auto-style your code whenever you click save.
  • Check if your code meets all of your style rules before you git commit.
  • Use and extend Google's Typescript style guidelines.

For video lovers

If you prefer to watch your content, or you're feeling a bit too sleepy to read ๐Ÿ˜ด, I've also made this guide into a video tutorial, you can find that here

Goal

At the end of the guide, you'll have a project that uses Next.JS and TypeScript to enforce Google's Typescript style guidelines when it comes to linting and formatting.

Guide

Creating the Next.JS App

Firstly we'll need to initialize a new Next.JS application. Run the following command from the command line to do-so:

npx create-next-app code-like-google

Now, let's change directory into your newly created Next app.

cd code-like-google

And open it up in Visual Studio Code!

code .

Cleaning up the example code

To clean up the example code provided by the create-next-app command, go ahead and:

  • Delete the api folder and it's contents
  • Delete _app.js

Setting up TypeScript

Next.js provides an integrated TypeScript experience out of the box, similar to an IDE.

To get started, create an empty tsconfig.json file in the root of your project.

Then run npm run dev from your command line.

shocked pikachu an error comes up!

Not to worry, we just need to install some packages to make it work. Run npm install --save-dev typescript @types/react @types/node

Now we can go back and change our index.js file to index.tsx, and run npm run dev again.

Next.js will automatically configure the tsconfig file with default values for Next.JS.

Let's modify the tsconfig.json file a little bit to match Google's standards. Modify it to the below:

{
  "compilerOptions": {
    "allowJs": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "declaration": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "lib": ["dom", "dom.iterable", "esnext"],
    "module": "esnext",
    "moduleResolution": "node",
    "noEmit": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitReturns": true,
    "pretty": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true,
    "target": "es2018"
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

Great! We are done setting up TypeScript, feel free to hover over each of these values to find out more about what they do or find out more on this document.

Setting up ESLint

What is ESLint?

ESLint statically analyzes your code to quickly find problems. Many problems ESLint finds can be automatically fixed. ESLint fixes are syntax-aware so you won't experience errors introduced by traditional find-and-replace algorithms.

In simple terms, ESLint makes suggestions for you to avoid bugs in your code, for example, suggesting that you add a return type to functions that don't have one, or recommending you remove console.log statements.

A full list of ESLint rules can be found here.

Some issues that ESLint finds are "auto-fixable", and later on, we'll be configuring ESLint to fix everything it can figure out itself whenever hit save.

To install ESLint, run

npm install eslint --save-dev

Then to configure ESLint, run:

npx eslint --init

Below are the settings that I chose to use in this guide, you can either choose to follow these settings or configure your own rules according to your personal preferences.

# Interactive
? How would you like to use ESLint? ...
> To check syntax, find problems, and enforce code style

? What type of modules does your project use? ...
> JavaScript modules (import/export)

? Which framework does your project use? ...
> React

? Does your project use TypeScript? ยป Yes

? Where does your code run? ...  
โˆš Browser

? How would you like to define a style for your project? ...
> Use a popular style guide

? Which style guide do you want to follow? ...
> Google: https://github.com/google/eslint-config-google

? What format do you want your config file to be in? ...
> JavaScript

eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest eslint-config-google@latest eslint@>=5.16.0 @typescript-eslint/parser@latest
? Would you like to install them now with npm? ยป Yes

What just happened?

We've installed a number of npm packages to configure ESLint to lint TypeScript code and also extended eslint-config-google to copy Google's linting rules.

You can take a look at what we created in the .eslintrc.js file. ESLint will look for eslintrc.* files and use them to enforce the rules that you have specified in the file.

If you followed the above example, we are extending Google's ruleset.

Side Note: During this step, I encountered a weird bug where react version was not being detected. I appended the following code inside my module.exports in eslintrc.js to resolve it:

settings: {
    react: {
      version: "latest",
    },
  },

Setting up Prettier

Prettier is a code formatter.

It removes all original styling and ensures that all outputted code conforms to a consistent style.

It'll do things like adding a semicolon to the end of every statement, or make sure your indentation is consistent.

To install prettier, run:

npm install --save-dev --save-exact prettier

To avoid format conflicts between ESLint and Prettier, we'll also need this package, called eslint-config-prettier. It turns off all ESLint rules that are unnecessary or might conflict with Prettier.

npm install --save-dev eslint-config-prettier

One last thing to make Prettier cooperate with ESLint; add "prettier" to the extends array in your eslintrc.js file. So it now should look like this:

extends: ["plugin:react/recommended", "google", "prettier"],

Make sure to put it last, so it gets the chance to override other configs.

To customize Prettier, create a .prettierrc file in the root of your directory.

Let's configure Prettier with the following in our .prettierrc file:

{
  "endOfLine": "lf",
  "printWidth": 80,
  "tabWidth": 2,
  "trailingComma": "es5"
}

Bonus: .prettierignore and .eslintignore

Similarly to .gitignore we can configure both ESLint and Prettier to ignore formatting or linting certain files or directories.

I set both of these files to the below:

.next
next-env.d.ts
node_modules
yarn.lock
package-lock.json
public

Configuring Visual Studio

Install Extensions

Inside Visual Studio, there are extensions for both ESLint and Prettier

Install both of these extensions in Visual Studio Code. (Ctrl + Shift + X to open the extensions window in Visual Studio Code.)

Configuring Preferences

To configure these extensions on a User level, go to File > Preferences > Settings or hit Ctrl + ,

image.png

Search "default formatter" in the search bar, and Editor: Default Fromatter to Prettier - Code formatter

Next, search "format on" in the search bar, and tick Editor: Format on Paste, and Editor: Format on Save.

image.png

To test if everything worked, go to index.tsx and press Alt + Shift + F. (you may be given an option to select default formatter here, select Prettier)

image.png

If we've set everything up correctly it should auto-format your code.

(You may need to restart VS Code).

If you're following along with the guide, ESLint will have a few things to say about our index.tsx file.

image.png

Not to worry, all that it's saying is that we need two things:

  • React imported if we want to use JSX
  • A JSDoc Comment to explain what the function does.

at the top, add :

import React from "react";

/**
 * Home: The Landing page of the web app
 * @return {JSX.Element} The JSX Code for the Home Page
 */

Formatting on save

We've implemented our standards and both ESLint and Prettier are going to help us stay consistent, but when there are auto-fixable problems ESLint can implement, I want them to be implemented whenever I hit save.

To achieve this, we can modify our VS Code settings for this project.

  1. Create a .vscode folder at the root of your directory
  2. Within that folder, create a settings.json file.
  3. Change your settings.json to:
    {
    "editor.formatOnPaste": true,
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.codeActionsOnSave": {
     "source.fixAll.eslint": true,
     "source.fixAll.format": true
    }
    }
    

This is going to first lint, then format all of our code every time we hit save. It's also enabled formatting code on pasting code into our files and set Prettier as the default formatter for this project.

Using husky to run checks on git commit

We are going to be using husky to perform the following whenever a git commit is made:

  • Check there are no Prettier warnings on our code.
  • Check there are no ESLint warnings on our code.
  • Check there are no errors compiling our code from TypeScript.
  • Check we can build our project using next build

To install husky, run:

npx husky-init

then

npm install

This will set up husky, modify package.json, and create a sample pre-commit hook that we are going to modify.

Firstly, let's modify package.json to add some scripts for what we would like to check.

In package.json, I have the following scripts:

"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "check-types": "tsc --pretty --noEmit",
    "check-format": "prettier --check .",
    "check-lint": "eslint . --ext ts --ext tsx --ext js",
    "format": "prettier --write .",
    "test-all": "npm run check-format && npm run check-lint && npm run check-types && npm run build",
    "prepare": "husky install"
  },

To summarise:

  • check-types runs TypeScript's tsc CLI command and pretty prints any warnings/errors.
  • check-format asks Prettier to check all of our files (excluding the ones in .prettierignore) for formatting issues.
  • check-lint asks ESLint to check for any linting warnings/errors on an .ts, .tsx or .js files.
  • format tells prettier to automatically re-write all of our files with proper formatting
  • test-all runs a number of the above commands in sequence.

Editing the pre-commit hook

The pre-commit hook that is created when we initialized husky does exactly what it says; it runs prior to executing a git commit.

For example, if you run a git commit command, this script would run first, then on its success, a commit would be executed. If the pre-commit hook fails, the commit does not go through.

This is what I've made the pre-commit script to contain:

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo '๐Ÿ—๏ธ๐Ÿ‘ท Styling, testing and building your project before committing'

# Check Prettier standards
npm run check-format ||
(
    echo '๐Ÿคข๐Ÿคฎ๐Ÿคข๐Ÿคฎ Its F**KING RAW - Your styling looks disgusting. ๐Ÿคข๐Ÿคฎ๐Ÿคข๐Ÿคฎ
            Prettier Check Failed. Run npm run format, add changes and try commit again.';
    false;
)

# Check ESLint Standards
npm run check-lint ||
(
        echo '๐Ÿ˜ค๐Ÿ€๐Ÿ‘‹๐Ÿ˜ค Get that weak s**t out of here! ๐Ÿ˜ค๐Ÿ€๐Ÿ‘‹๐Ÿ˜ค 
                ESLint Check Failed. Make the required changes listed above, add changes and try to commit again.'
        false; 
)

# Check tsconfig standards
npm run check-types ||
(
    echo '๐Ÿคก๐Ÿ˜‚โŒ๐Ÿคก Failed Type check. ๐Ÿคก๐Ÿ˜‚โŒ๐Ÿคก
            Are you seriously trying to write that? Make the changes required above.'
    false;
)

# If everything passes... Now we can commit
echo '๐Ÿค”๐Ÿค”๐Ÿค”๐Ÿค”... Alright... Code looks good to me... Trying to build now. ๐Ÿค”๐Ÿค”๐Ÿค”๐Ÿค”'

npm run build ||
(
    echo 'โŒ๐Ÿ‘ท๐Ÿ”จโŒ Better call Bob... Because your build failed โŒ๐Ÿ‘ท๐Ÿ”จโŒ
            Next build failed: View the errors above to see why. 
    '
    false;
)

# If everything passes... Now we can commit
echo 'โœ…โœ…โœ…โœ… You win this time... I am committing this now. โœ…โœ…โœ…โœ…'

I like to have a little fun with my messages as you can see... In a nutshell, all we are saying in this file is, before a commit:

  • Check Prettier standards looks good (otherwise fail commit)
  • Check ESLint standards looks good (otherwise fail commit)
  • Check tsconfig standards looks good (otherwise fail commit)
  • Try produce a next build (otherwise fail commit)

Conclusion

That's it! Now every time we save our file we'll auto-lint and auto-format. Not only that, every time we try to make a commit to git, we first have to pass a series of tests and face the gauntlet of being roasted by our own emojis!

Thanks for reading!

Find me at:

Buy me a coffee โ˜•

YouTube: youtube.com/channel/UCJae_agpt9S3qwWNED0KHcQ

Twitter: twitter.com/JarrodWattsDev

GitHub: github.com/jarrodwatts

LinkedIn: linkedin.com/in/jarrodwatts

Website: jarrodwatts.com

Did you find this article valuable?

Support Jarrod Watts by becoming a sponsor. Any amount is appreciated!