Skip to content
This repository has been archived by the owner on Mar 30, 2022. It is now read-only.

Project-Setup/nextjs_redux_toolkit

Repository files navigation

Example Project Setup with NextJs, Redux-Toolkit, Typescript, Eslint, Jest and Emotion.

Dependency Versions

  • NextJs v10.0.5
  • react v17.0.1
  • Typescript v4.1.3
  • @emotion/react v11.1.4

Usage of this example setup

  1. setup node env
    nvm use || npm install
  2. remove unwanted files in public/, src/
  3. add .env and other .env files
  4. preview dev progress on http://localhost:3000/
    npm run dev
  5. read Setup for notes

Setup

  1. install nvm in the os
  2. nvm install node
    git init
  3. add .gitignore
  4. node -v > .nvmrc
  5. npm init -y
  1. npm i -S next react react-dom
  2. add a script to your package.json like this:
    {
      "scripts": {
        "dev": "next dev",
        "build": "next build",
        "start": "next start"
      }
    }
  1. npm i -D typescript @types/react @types/node
  2. create tsconfig.json
    {
      "compilerOptions": {
        "target": "esnext",
        "lib": ["dom", "dom.iterable", "esnext"],
        "allowJs": true,
        "allowSyntheticDefaultImports": true,
        "alwaysStrict": true,
        "skipLibCheck": true,
        "strict": false,
        "forceConsistentCasingInFileNames": true,
        "noEmit": true,
        "noFallthroughCasesInSwitch": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "esModuleInterop": true,
        "module": "esnext",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "jsx": "preserve",
        "baseUrl": "./src"
      },
      "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
      "exclude": ["node_modules", "next.config.js"]
    }
  1. create src/pages folder (or pages)
  2. create pages.tsx under src/pages/ (i.e. src/pages/index.tsx for / route)
  1. npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react eslint-import-resolver-typescript
    npm i -D eslint-config-airbnb eslint-plugin-jsx-a11y eslint-plugin-import eslint-plugin-react-hooks
    npm i -D prettier eslint-config-prettier eslint-plugin-prettier
  2. create .eslintrc.js
    module.exports = {
      env: {
        browser: true,
        node: true,
        es2020: true,
        jest: true,
      },
      parser: '@typescript-eslint/parser', // Specifies the ESLint parser
      parserOptions: {
        ecmaVersion: 2020,
        sourceType: 'module',
        ecmaFeatures: {
          jsx: true,
        },
      },
      plugins: ['@typescript-eslint', 'react', 'react-hooks', 'prettier'],
      extends: [
        'airbnb',
        'plugin:@typescript-eslint/recommended',
        'plugin:react/recommended',
        'plugin:import/errors',
        'plugin:import/warnings',
        'plugin:import/typescript',
        'prettier',
        'prettier/@typescript-eslint',
        'prettier/react',
        'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
      ],
      rules: {
        '@typescript-eslint/no-unused-vars': [
          'error',
          {
            vars: 'all',
            args: 'after-used',
            ignoreRestSiblings: false,
          },
        ],
        '@typescript-eslint/no-explicit-any': 0,
        '@typescript-eslint/explicit-function-return-type': 0,
        '@typescript-eslint/no-namespace': 0,
        '@typescript-eslint/explicit-module-boundary-types': 0,
        'import/extensions': [
          1,
          { extensions: ['.js', '.jsx', '.ts', '.tsx'] },
        ],
        'import/no-extraneous-dependencies': [
          'error',
          {
            devDependencies: true,
          },
        ],
        'react/jsx-filename-extension': [
          1,
          { extensions: ['.js', '.jsx', '.ts', '.tsx'] },
        ],
        'react/react-in-jsx-scope': 0,
        'react/jsx-first-prop-new-line': 0,
        'react/prop-types': 0,
        'react/jsx-props-no-spreading': [2, { custom: 'ignore' }],
        'jsx-a11y/anchor-is-valid': [
          'error',
          {
            components: ['Link'],
            specialLink: ['hrefLeft', 'hrefRight'],
            aspects: ['invalidHref', 'preferButton'],
          },
        ],
        'prettier/prettier': 2,
        'react-hooks/rules-of-hooks': 2,
        'react-hooks/exhaustive-deps': 2,
        'no-bitwise': 2,
      },
      settings: {
        'import/resolver': {
          node: {
            extensions: ['.js', '.jsx', '.ts', '.tsx'],
          },
          typescript: {},
        },
        react: {
          version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
        },
      },
    };
  3. create .prettierrc.js
    module.exports = {
      semi: true,
      trailingComma: 'es5',
      singleQuote: true,
      printWidth: 80,
      tabWidth: 2,
    };
  1. npm i -D jest babel-jest @types/jest @next/env
  2. add scripts in package.json

    "scripts": {
      "test": "jest",
      "test:watch": "jest --watch",
      "test:coverage": "jest --coverage"
    },
  3. create jest.config.js

    module.exports = {
      moduleFileExtensions: ['ts', 'tsx', 'js'],
      testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|js?|tsx?|ts?)$',
      globals: {
        NODE_ENV: 'test',
      },
      snapshotSerializers: ['enzyme-to-json/serializer'],
      transform: {
        '^.+\\.(j|t)sx?$': 'babel-jest',
      },
      coveragePathIgnorePatterns: [
        '/node_modules/',
        'jest.setup.js',
        '<rootDir>/configs/',
        'jest.config.js',
        '.json',
        '.snap',
      ],
      setupFiles: ['<rootDir>/jest/jest.setup.js'],
      coverageReporters: ['json', 'lcov', 'text', 'text-summary'],
      moduleNameMapper: {
        '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
          '<rootDir>/__mocks__/mocks.js',
        '\\.(css|less|scss)$': '<rootDir>/__mocks__/mocks.js',
      },
      moduleDirectories: ['node_modules', 'src'],
    };
  4. create babel.config.js

    module.exports = {
      presets: ['next/babel'],
    };
  5. create jest/jest.setup.js

    import { join } from 'path';
    import { loadEnvConfig } from '@next/env';
    
    // to load '.env' files in test
    loadEnvConfig(join(__dirname, '../'));
  6. change env in .eslintrc.js

    env: {
      browser: true,
      node: true,
      jest: true
    },
  1. npm i -S @emotion/core
    npm i -D @emotion/babel-plugin @emotion/eslint-plugin @emotion/jest
  2. change babel.config.js

    module.exports = {
      presets: [
        [
          'next/babel',
          {
            'preset-react': {
              runtime: 'automatic',
              importSource: '@emotion/react',
            },
          },
        ],
      ],
      plugins: ['@emotion/babel-plugin'],
    };
  3. add rules and plugins to .eslintrc.js

    module.exports = {
      // ...
      rules: {
        // ...
        '@emotion/no-vanilla': 'error',
        '@emotion/import-from-emotion': 'error',
        '@emotion/styled-import': 'error',
      },
      // ...
      plugins: [
        '@emotion',
        // ...
      ],
      // ...
    };
  4. add jest/jest.setupAfterEnv.js

    import { matchers } from '@emotion/jest';
    
    expect.extend(matchers);
  5. add serializers and setup files to jest/jest.config.js

    // ...
    snapshotSerializers: ['@emotion/jest/serializer'],
    // ...
    setupFilesAfterEnv: ['<rootDir>/jest.setupAfterEnv.js'],
    // ...
  6. add to tsconfig.json

    {
      "compilerOptions": {
        "jsxImportSource": "@emotion/react"
      }
    }

(deploy to /docs intead of using gh-pages branch; replace {folder} with the project name in github repo)

  1. add .env.production
    NEXT_PUBLIC_LINK_PREFIX=/{folder}
  2. create LINK_PREFIX in next.config.js
    const LINK_PREFIX = process.env.NEXT_PUBLIC_LINK_PREFIX || '';
    module.exports = () => ({
      basePath: LINK_PREFIX,
    });
  3. change scripts in package.json
    {
      "scripts": {
        "export": "NODE_ENV=production npm run build && next export -o docs && touch docs/.nojekyll"
      }
    }
  1. npm i -S react-redux @reduxjs/toolkit next-redux-wrapper
    npm i -D @types/react-redux @types/webpack-env
  2. create custom rootReducer, makeStore, wrapper similar to ones in files src/features/redux/reducers.tsx and src/features/redux/store.tsx and apply them in src/pages/_app.tsx