├── cypress.json
├── .husky
└── pre-commit
├── public
├── favicon.ico
└── vercel.svg
├── .prettierrc.json
├── next.config.js
├── cypress
├── fixtures
│ └── example.json
├── integration
│ └── app.spec.ts
├── support
│ ├── index.ts
│ └── commands.ts
└── plugins
│ └── index.ts
├── next-env.d.ts
├── pages
├── _app.tsx
├── api
│ └── hello.ts
├── about.tsx
└── index.tsx
├── .eslintrc.json
├── styles
├── globals.css
└── Home.module.css
├── __tests__
└── index.test.tsx
├── lint-staged.config.js
├── .gitignore
├── tsconfig.json
├── jest.config.ts
├── .github
└── workflows
│ └── test.yml
├── LICENSE
├── package.json
└── README.md
/cypress.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn lint-staged
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wk0/boilerplate-next/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "trailingComma": "es5",
4 | "singleQuote": true,
5 | "tabWidth": 2,
6 | "useTabs": false
7 | }
8 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | }
5 |
6 | module.exports = nextConfig
7 |
--------------------------------------------------------------------------------
/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css'
2 | import type { AppProps } from 'next/app'
3 |
4 | function MyApp({ Component, pageProps }: AppProps) {
5 | return
6 | }
7 |
8 | export default MyApp
9 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["@typescript-eslint"],
3 | "extends": [
4 | "next/core-web-vitals",
5 | "plugin:jest-dom/recommended",
6 | "plugin:@typescript-eslint/recommended",
7 | "prettier"
8 | ],
9 | "rules": {
10 | "@typescript-eslint/no-unused-vars": "error"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | padding: 0;
4 | margin: 0;
5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
7 | }
8 |
9 | a {
10 | color: inherit;
11 | text-decoration: none;
12 | }
13 |
14 | * {
15 | box-sizing: border-box;
16 | }
17 |
--------------------------------------------------------------------------------
/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 | import type { NextApiRequest, NextApiResponse } from 'next'
3 |
4 | type Data = {
5 | name: string
6 | }
7 |
8 | export default function handler(
9 | req: NextApiRequest,
10 | res: NextApiResponse
11 | ) {
12 | res.status(200).json({ name: 'John Doe' })
13 | }
14 |
--------------------------------------------------------------------------------
/__tests__/index.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react'
2 | import Home from '../pages/index'
3 | import '@testing-library/jest-dom'
4 |
5 | describe('Home', () => {
6 | it('renders a heading', () => {
7 | render()
8 |
9 | const heading = screen.getByRole('heading', {
10 | name: /Home Page/i,
11 | })
12 |
13 | expect(heading).toBeInTheDocument()
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // Type check TypeScript files
3 | '**/*.(ts|tsx)': () => 'yarn tsc --noEmit',
4 |
5 | // Lint then format TypeScript and JavaScript files
6 | '**/*.(ts|tsx|js)': (filenames) => [
7 | `yarn eslint --fix ${filenames.join(' ')}`,
8 | `yarn prettier --write ${filenames.join(' ')}`,
9 | ],
10 |
11 | // Format MarkDown and JSON
12 | '**/*.(md|json)': (filenames) =>
13 | `yarn prettier --write ${filenames.join(' ')}`,
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env.local
30 | .env.development.local
31 | .env.test.local
32 | .env.production.local
33 |
34 | # vercel
35 | .vercel
36 |
37 | # typescript
38 | *.tsbuildinfo
39 |
--------------------------------------------------------------------------------
/cypress/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | // cypress/integration/app.spec.js
2 |
3 | describe('Navigation', () => {
4 | it('should navigate to the about page', () => {
5 | // Start from the index page
6 | cy.visit('http://localhost:3000/')
7 |
8 | // Find a link with an href attribute containing "about" and click it
9 | cy.get('a[href*="about"]').click()
10 |
11 | // The new url should include "/about"
12 | cy.url().should('include', '/about')
13 |
14 | // The new page should contain an h1 with "About page"
15 | cy.get('h1').contains('About Page')
16 | })
17 | })
18 |
19 | const asModule = {}
20 | export default asModule
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "types": ["jest", "node", "@types/testing-library__jest-dom"]
18 | },
19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
20 | "exclude": ["node_modules", "cypress"]
21 | }
22 |
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | import nextJest from "next/jest";
2 | // Sync object
3 | const createJestConfig = nextJest({
4 | // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
5 | dir: "./",
6 | });
7 |
8 | // Add any custom config to be passed to Jest
9 | const customJestConfig = {
10 | // Add more setup options before each test is run
11 | setupFilesAfterEnv: ["@testing-library/jest-dom"], // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work
12 | moduleDirectories: ["node_modules", "/"],
13 | testEnvironment: "jest-environment-jsdom",
14 | modulePathIgnorePatterns: ["cypress"],
15 | };
16 |
17 | module.exports = createJestConfig(customJestConfig);
18 |
--------------------------------------------------------------------------------
/cypress/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/pages/about.tsx:
--------------------------------------------------------------------------------
1 | import type { NextPage } from 'next'
2 | import Head from 'next/head'
3 | import Link from 'next/link'
4 |
5 | const About: NextPage = () => {
6 | return (
7 |
8 |
9 |
Next App Typescript Boilerplate
10 |
11 |
12 |
13 |
14 |
15 |
20 |
21 |
22 | About Page
23 |
24 |
25 |
28 |
29 | )
30 | }
31 |
32 | export default About
33 |
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import type { NextPage } from 'next'
2 | import Head from 'next/head'
3 | import Link from 'next/link'
4 |
5 | const Home: NextPage = () => {
6 | return (
7 |
8 |
9 |
Next App Typescript Boilerplate
10 |
11 |
12 |
13 |
14 |
15 |
20 |
21 |
22 | Home Page
23 |
24 |
25 |
28 |
29 | )
30 | }
31 |
32 | export default Home
33 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: CI - Test
2 | on:
3 | push:
4 | branches:
5 | - '*'
6 | pull_request:
7 | branches:
8 | - '*'
9 |
10 | jobs:
11 | test:
12 | name: Setup
13 | runs-on: ubuntu-latest
14 | timeout-minutes: 5
15 | steps:
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-node@v2-beta
18 | with:
19 | node-version: '16.6.2'
20 | check-latest: true
21 |
22 | - name: Install Npm Dependencies
23 | run: yarn install
24 |
25 | - name: Build App
26 | run: yarn build
27 |
28 | - name: Run tests with jest
29 | run: yarn test:ci
30 |
31 | - name: Cypress.io
32 | uses: cypress-io/github-action@v2
33 | with:
34 | start: yarn start
35 | wait-on: 'http://localhost:3000'
--------------------------------------------------------------------------------
/cypress/plugins/index.ts:
--------------------------------------------------------------------------------
1 | ///
2 | // ***********************************************************
3 | // This example plugins/index.js can be used to load plugins
4 | //
5 | // You can change the location of this file or turn off loading
6 | // the plugins file with the 'pluginsFile' configuration option.
7 | //
8 | // You can read more here:
9 | // https://on.cypress.io/plugins-guide
10 | // ***********************************************************
11 |
12 | // This function is called when a project is opened or re-opened (e.g. due to
13 | // the project's config changing)
14 |
15 | /**
16 | * @type {Cypress.PluginConfig}
17 | */
18 | // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
19 | const loadConfig = (_on: any, _config: any) => {
20 | // `on` is used to hook into various events Cypress emits
21 | // `config` is the resolved Cypress config
22 | }
23 | export default loadConfig
24 |
--------------------------------------------------------------------------------
/cypress/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add('login', (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
26 | const asModule = {}
27 | export default asModule
28 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 William Kelly
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "boilerplate-next",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "test": "jest --watch",
11 | "test:ci": "jest --ci",
12 | "cypress": "cypress open"
13 | },
14 | "husky": {
15 | "hooks": {
16 | "pre-commit": "lint-staged"
17 | }
18 | },
19 | "dependencies": {
20 | "next": "12.1.0",
21 | "react": "17.0.2",
22 | "react-dom": "17.0.2"
23 | },
24 | "devDependencies": {
25 | "@testing-library/jest-dom": "^5.16.2",
26 | "@testing-library/react": "^12.1.3",
27 | "@types/cypress": "^1.1.3",
28 | "@types/node": "17.0.18",
29 | "@types/react": "17.0.39",
30 | "@types/testing-library__jest-dom": "^5.14.2",
31 | "@types/testing-library__react": "^10.2.0",
32 | "@typescript-eslint/eslint-plugin": "^5.12.0",
33 | "cypress": "^9.5.0",
34 | "eslint": "8.9.0",
35 | "eslint-config-next": "12.1.0",
36 | "eslint-config-prettier": "^8.3.0",
37 | "eslint-plugin-jest-dom": "^4.0.1",
38 | "husky": "^7.0.4",
39 | "jest": "^27.5.1",
40 | "lint-staged": "^12.3.4",
41 | "prettier": "^2.5.1",
42 | "ts-node": "^10.5.0",
43 | "typescript": "4.5.5"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Boilerplate-Next
2 |
3 | Follow along with how I made this via the step-by-step post here on [Medium](https://wk0.medium.com/create-a-typescript-nextjs-project-with-jest-cypress-adbbcf237747)
4 |
5 | Bare-bones
6 |
7 | [](https://vercel.com/new/clone?repository-url=https://github.com/wk0/boilerplate-next)
8 |
9 | ---
10 |
11 | With TailwindCSS
12 |
13 | Step by step guide to setting up with Tailwind: [Medium](https://wk0.medium.com/adding-tailwind-to-a-nextjs-typescript-project-d1eba5699c4d)
14 |
15 | [](https://github.com/wk0/boilerplate-next/tree/tailwind)
16 |
17 | ---
18 |
19 | With Web3
20 |
21 | Step by step guide to setting up with Web3: [Medium](https://wk0.medium.com/adding-web3-to-our-nextjs-typescript-project-861e9ed5feaf)
22 |
23 | [](https://github.com/wk0/boilerplate-next/tree/web3-eth)
24 |
25 | ---
26 |
27 | As a Monorepo
28 |
29 | How to convert: [Medium](https://medium.com/p/bf4007fdfa87)
30 |
31 | [](https://github.com/wk0/boilerplate-next/tree/as-workspace)
32 |
33 |
34 | ---
35 |
36 | With Contracts
37 |
38 | How to add hardhat: [Medium](https://medium.com/@wk0/integrating-smart-contracts-using-hardhat-with-nextjs-typescript-7206890b9cd8)
39 |
40 | [](https://github.com/wk0/boilerplate-next/tree/with-contracts)
41 |
--------------------------------------------------------------------------------
/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 0 2rem;
3 | }
4 |
5 | .main {
6 | min-height: 100vh;
7 | padding: 4rem 0;
8 | flex: 1;
9 | display: flex;
10 | flex-direction: column;
11 | justify-content: center;
12 | align-items: center;
13 | }
14 |
15 | .footer {
16 | display: flex;
17 | flex: 1;
18 | padding: 2rem 0;
19 | border-top: 1px solid #eaeaea;
20 | justify-content: center;
21 | align-items: center;
22 | }
23 |
24 | .footer a {
25 | display: flex;
26 | justify-content: center;
27 | align-items: center;
28 | flex-grow: 1;
29 | }
30 |
31 | .title a {
32 | color: #0070f3;
33 | text-decoration: none;
34 | }
35 |
36 | .title a:hover,
37 | .title a:focus,
38 | .title a:active {
39 | text-decoration: underline;
40 | }
41 |
42 | .title {
43 | margin: 0;
44 | line-height: 1.15;
45 | font-size: 4rem;
46 | }
47 |
48 | .title,
49 | .description {
50 | text-align: center;
51 | }
52 |
53 | .description {
54 | margin: 4rem 0;
55 | line-height: 1.5;
56 | font-size: 1.5rem;
57 | }
58 |
59 | .code {
60 | background: #fafafa;
61 | border-radius: 5px;
62 | padding: 0.75rem;
63 | font-size: 1.1rem;
64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
65 | Bitstream Vera Sans Mono, Courier New, monospace;
66 | }
67 |
68 | .grid {
69 | display: flex;
70 | align-items: center;
71 | justify-content: center;
72 | flex-wrap: wrap;
73 | max-width: 800px;
74 | }
75 |
76 | .card {
77 | margin: 1rem;
78 | padding: 1.5rem;
79 | text-align: left;
80 | color: inherit;
81 | text-decoration: none;
82 | border: 1px solid #eaeaea;
83 | border-radius: 10px;
84 | transition: color 0.15s ease, border-color 0.15s ease;
85 | max-width: 300px;
86 | }
87 |
88 | .card:hover,
89 | .card:focus,
90 | .card:active {
91 | color: #0070f3;
92 | border-color: #0070f3;
93 | }
94 |
95 | .card h2 {
96 | margin: 0 0 1rem 0;
97 | font-size: 1.5rem;
98 | }
99 |
100 | .card p {
101 | margin: 0;
102 | font-size: 1.25rem;
103 | line-height: 1.5;
104 | }
105 |
106 | .logo {
107 | height: 1em;
108 | margin-left: 0.5rem;
109 | }
110 |
111 | @media (max-width: 600px) {
112 | .grid {
113 | width: 100%;
114 | flex-direction: column;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------