Pavan Jadhaw
Blog

Setting up nextjs typescript project

Setup

It is recommended creating a new Next.js app using create-next-app, which sets up everything automatically for you.

yarn create next-app

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:

touch tsconfig.json

Next.js will automatically configure this file with default values when starting next dev server.

npm run dev
# You'll see instructions like these:
#
# Please install typescript, @types/react, and @types/node by running:
#
# yarn add --dev typescript @types/react @types/node
#
# ...

Install typescript & types for node and react.

yarn add --dev typescript @types/react @types/node

You're now ready to start converting files from .js to .tsx and leveraging the benefits of TypeScript!.

Eslint

First step is to make sure you've got the required packages installed:

yarn add -D eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin

Next, create a .eslintrc config file in the root of your project, and populate it with the following:

{
"root": true,
"env": {
"browser": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parserOptions": {
"project": "tsconfig.json"
}
}

This is about the smallest config file required. We'll be extending it in next section.

Configuring Eslint

Extending the original base of rules (plugin:@next/next/recommended) is highly recommended to catch and fix significant Next.js issues in your application.

{
"root": true,
"env": {
"browser": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"plugin:@next/next/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"project": "tsconfig.json"
}
}

Adding react specific eslint rules.

yarn add --dev eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y
{
"root": true,
"env": {
"browser": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"plugin:@next/next/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended"
],
"parserOptions": {
"project": "tsconfig.json"
}
}

Tuning the rules and turning up the strictness.

{
"root": true,
"env": {
"browser": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"plugin:@next/next/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended"
],
"rules": {
"prefer-const": "error",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"jsx-a11y/anchor-is-valid": "off"
},
"parserOptions": {
"project": "tsconfig.json"
}
}

'react/prop-types': 'off' - We will be using typescript types for props. 'react/react-in-jsx-scope': 'off' - nextjs doesnt require us to import React in every component.

Prettier

First, install Prettier locally:

yarn add --dev prettier

Then, create config file .prettierrc in the root of your project to let editors and other tooling know you are using Prettier:

{
"tabWidth": 2,
"printWidth": 80,
"semi": true,
"useTabs": false,
"singleQuote": true,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"endOfLine": "lf",
"arrowParens": "avoid",
"trailingComma": "all"
}

Configuring eslint to work with prettier.

yarn add --dev eslint-config-prettier

Edit eslintrc and add prettier plugin.

{
"root": true,
"env": {
"browser": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"plugin:@next/next/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended",
"prettier"
],
"rules": {
"prefer-const": "error",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"jsx-a11y/anchor-is-valid": "off"
},
"parserOptions": {
"project": "tsconfig.json"
}
}

Note: Make sure prettier vscode extension is installed and format on save is enabled.

Bonus: Pre-commit hook

You can use Prettier with a pre-commit tool. This can re-format your files that are marked as “staged” via git add before you commit.
Make sure Prettier is installed and is in your devDependencies before you proceed.

npx mrm lint-staged

This will install husky and lint-staged, then add a configuration to the project’s package.json that will automatically format supported files in a pre-commit hook.

Bonus: Absolute Paths

Absolute paths mean that you will have to change less if you need to move a component. Also it makes it easier to find out where everything is getting pulled from.

import Button from '@components/Button';
import posts from '@utils/api';

To setup absolute paths edit tsconfig.json and add the following lines, also toggle strict to true while we are at it.

{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"@components/*": ["components/*"]
},
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

Bonus: Pinning Node engines

If you are using volta The volta pin command allows you to choose your Node engine and package manager versions for a project:

volta pin node yarn

Volta stores this in your package.json so you can commit your choice of tools to version control:

"volta": {
"node": "14.16.0",
"yarn": "1.22.10"
},

This way, everyone who uses Volta to work on the project automatically gets the same version you selected.

node --version # v14.16.0
yarn --version # 1.22.10