├── .jest
└── setup.ts
├── src
├── components
│ ├── index.ts
│ └── HelloWorld
│ │ ├── index.ts
│ │ ├── HelloWorld.tsx
│ │ └── HelloWorld.test.tsx
├── vite-env.d.ts
├── App.tsx
└── main.tsx
├── .prettierignore
├── .prettierrc
├── .husky
└── pre-commit
├── generators
├── templates
│ ├── hook
│ │ ├── index.ts.hbs
│ │ ├── hook.ts.hbs
│ │ └── test.ts.hbs
│ └── component
│ │ ├── index.ts.hbs
│ │ ├── component.tsx.hbs
│ │ └── test.tsx.hbs
├── plopfile.cjs
├── hook.generator.cjs
└── component.generator.cjs
├── .vscode
└── settings.json
├── .github
├── dependabot.yml
└── workflows
│ └── ci.yml
├── .editorconfig
├── tsconfig.node.json
├── babel.config.cjs
├── vite.config.ts
├── .gitignore
├── index.html
├── jest.config.cjs
├── tsconfig.json
├── .eslintrc.json
├── public
└── vite.svg
├── README.md
└── package.json
/.jest/setup.ts:
--------------------------------------------------------------------------------
1 | import "@testing-library/jest-dom";
2 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './HelloWorld'
2 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | !.jest
2 | /dist
3 | /node_modules
4 | /public
5 | /generators
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "none",
3 | "semi": false,
4 | "singleQuote": true
5 | }
6 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx --no-install lint-staged
5 |
--------------------------------------------------------------------------------
/generators/templates/hook/index.ts.hbs:
--------------------------------------------------------------------------------
1 | export { default as {{camelCase name}} } from './{{camelCase name}}'
2 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | function App() {
2 | return
React JavaScript Boilerplate
3 | }
4 |
5 | export default App
6 |
--------------------------------------------------------------------------------
/generators/templates/hook/hook.ts.hbs:
--------------------------------------------------------------------------------
1 | function {{camelCase name}}() {
2 | return true
3 | }
4 |
5 | export default {{camelCase name}}
6 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": false,
3 | "editor.codeActionsOnSave": {
4 | "source.fixAll.eslint": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/HelloWorld/index.ts:
--------------------------------------------------------------------------------
1 | export type { Props as HelloWorldProps } from './HelloWorld'
2 | export { default as HelloWorld } from './HelloWorld'
3 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: monthly
7 | open-pull-requests-limit: 15
8 |
--------------------------------------------------------------------------------
/generators/templates/component/index.ts.hbs:
--------------------------------------------------------------------------------
1 | export type { Props as {{pascalCase name}}Props } from './{{pascalCase name}}'
2 | export { default as {{pascalCase name}} } from './{{pascalCase name}}'
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/babel.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ['@babel/preset-typescript'],
4 | ['@babel/preset-env', { modules: 'auto' }],
5 | [
6 | '@babel/preset-react',
7 | {
8 | runtime: 'automatic'
9 | }
10 | ]
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 |
5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
6 |
7 |
8 |
9 | )
10 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | resolve: {
8 | alias: {
9 | '@': '/src'
10 | }
11 | }
12 | })
13 |
--------------------------------------------------------------------------------
/generators/plopfile.cjs:
--------------------------------------------------------------------------------
1 | const componentGenerator = require('./component.generator.cjs')
2 | const hookGenerator = require('./hook.generator.cjs')
3 |
4 | module.exports = function generator(plop) {
5 | plop.setGenerator('component', componentGenerator)
6 | plop.setGenerator('hook', hookGenerator)
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/HelloWorld/HelloWorld.tsx:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react'
2 |
3 | export type Props = {
4 | children: ReactNode
5 | }
6 |
7 | function HelloWorld(props: Props) {
8 | const { children } = props
9 |
10 | return {children}
11 | }
12 |
13 | export default HelloWorld
14 |
--------------------------------------------------------------------------------
/generators/templates/component/component.tsx.hbs:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react'
2 |
3 | export type Props = {
4 | children: ReactNode
5 | }
6 |
7 | function {{pascalCase name}}(props: Props) {
8 | const { children } = props
9 |
10 | return {children}
11 | }
12 |
13 | export default {{pascalCase name}}
14 |
--------------------------------------------------------------------------------
/generators/templates/hook/test.ts.hbs:
--------------------------------------------------------------------------------
1 | import { renderHook } from '@testing-library/react'
2 | import {{camelCase name}} from './{{camelCase name}}'
3 |
4 | describe('{{camelCase name}} | hook | integration test', () => {
5 | it('...', () => {
6 | const { result } = renderHook({{camelCase name}})
7 |
8 | expect(result.current).toBe(true)
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | !.vscode/extensions.json
17 | .idea
18 | .DS_Store
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
25 | # jest
26 | coverage
27 |
--------------------------------------------------------------------------------
/src/components/HelloWorld/HelloWorld.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react'
2 | import HelloWorld from './HelloWorld'
3 |
4 | describe('HelloWorld | component | unit test', () => {
5 | it('should render with success', () => {
6 | render(React JavaScript Boilerplate)
7 |
8 | expect(screen.getByText('React JavaScript Boilerplate')).toBeInTheDocument()
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | React JavaScript Boilerplate
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/generators/templates/component/test.tsx.hbs:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react'
2 | import {{pascalCase name}} from './{{pascalCase name}}'
3 |
4 | describe('{{pascalCase name}} | component | unit test', () => {
5 | it('should render with success', () => {
6 | render(<{{pascalCase name}}>{{pascalCase name}}{{pascalCase name}}>)
7 |
8 | expect(screen.getByText('{{pascalCase name}}')).toBeInTheDocument()
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/jest.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: 'jsdom',
3 | testPathIgnorePatterns: ['/dist/', '/node_modules/', '/public/'],
4 | collectCoverageFrom: [
5 | 'src/**/*.ts(x)?',
6 | '!src/**/index.ts',
7 | '!src/main.tsx',
8 | '!src/vite-env.d.ts'
9 | ],
10 | modulePaths: ['/src/'],
11 | setupFilesAfterEnv: ['/.jest/setup.ts'],
12 | transform: {
13 | '^.+\\.tsx?$': 'babel-jest'
14 | },
15 | moduleNameMapper: {
16 | '@/(.*)': '/src/$1'
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "target": "ESNext",
5 | "useDefineForClassFields": true,
6 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
7 | "allowJs": false,
8 | "skipLibCheck": true,
9 | "esModuleInterop": false,
10 | "allowSyntheticDefaultImports": true,
11 | "strict": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "module": "ESNext",
14 | "moduleResolution": "Node",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "noEmit": true,
18 | "jsx": "react-jsx",
19 | "paths": {
20 | "@/*": ["./src/*"]
21 | }
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/generators/hook.generator.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | description: 'application hook',
3 | prompts: [
4 | {
5 | type: 'input',
6 | name: 'name',
7 | message:
8 | "What's the hook's name? If the hook has more than one word, insert a space between them."
9 | }
10 | ],
11 | actions: [
12 | {
13 | type: 'add',
14 | path: '../src/hooks/{{camelCase name}}/index.ts',
15 | templateFile: './templates/hook/index.ts.hbs'
16 | },
17 | {
18 | type: 'add',
19 | path: '../src/hooks/{{camelCase name}}/{{camelCase name}}.ts',
20 | templateFile: './templates/hook/hook.ts.hbs'
21 | },
22 | {
23 | type: 'add',
24 | path: '../src/hooks/{{camelCase name}}/{{camelCase name}}.test.ts',
25 | templateFile: './templates/hook/test.ts.hbs'
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true,
5 | "jest": true,
6 | "node": true
7 | },
8 | "settings": {
9 | "react": {
10 | "version": "detect"
11 | }
12 | },
13 | "extends": [
14 | "eslint:recommended",
15 | "plugin:react/recommended",
16 | "plugin:@typescript-eslint/recommended",
17 | "plugin:prettier/recommended"
18 | ],
19 | "overrides": [
20 | ],
21 | "parser": "@typescript-eslint/parser",
22 | "parserOptions": {
23 | "ecmaVersion": "latest",
24 | "sourceType": "module"
25 | },
26 | "plugins": [
27 | "react",
28 | "react-hooks",
29 | "@typescript-eslint"
30 | ],
31 | "rules": {
32 | "react-hooks/rules-of-hooks": "error",
33 | "react-hooks/exhaustive-deps": "warn",
34 | "react/react-in-jsx-scope": "off"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/generators/component.generator.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | description: 'application component',
3 | prompts: [
4 | {
5 | type: 'input',
6 | name: 'name',
7 | message:
8 | 'What is the component name? If the component has more than one word, insert a space between them.'
9 | }
10 | ],
11 | actions: [
12 | {
13 | type: 'add',
14 | path: '../src/components/{{pascalCase name}}/index.ts',
15 | templateFile: './templates/component/index.ts.hbs'
16 | },
17 | {
18 | type: 'add',
19 | path: '../src/components/{{pascalCase name}}/{{pascalCase name}}.tsx',
20 | templateFile: './templates/component/component.tsx.hbs'
21 | },
22 | {
23 | type: 'add',
24 | path: '../src/components/{{pascalCase name}}/{{pascalCase name}}.test.tsx',
25 | templateFile: './templates/component/test.tsx.hbs'
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 | on: [pull_request]
3 |
4 | jobs:
5 | build:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - name: Checkout Repository
9 | uses: actions/checkout@v3
10 |
11 | - name: Setup Node
12 | uses: actions/setup-node@v1
13 | with:
14 | node-version: 20.x
15 |
16 | - uses: actions/cache@v3
17 | id: yarn-cache
18 | with:
19 | path: |
20 | ~/cache
21 | !~/cache/exclude
22 | **/node_modules
23 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
24 | restore-keys: |
25 | ${{ runner.os }}-yarn-
26 |
27 | - name: Install dependencies
28 | run: yarn install
29 |
30 | - name: Linting
31 | run: yarn lint
32 |
33 | - name: Test
34 | run: yarn test:ci
35 |
36 | - name: Build
37 | run: yarn build
38 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React TypeScript Boilerplate
2 |
3 | ## What is inside?
4 |
5 | - [Vite](https://vitejs.dev)
6 | - [EditorConfig](https://editorconfig.org)
7 | - [ESLint](https://eslint.org)
8 | - [Prettier](https://prettier.io)
9 | - [Jest](https://jestjs.io)
10 | - [React Testing Library](https://testing-library.com/docs/react-testing-library/intro)
11 | - [Plop js](https://plopjs.com)
12 |
13 | ## Getting Started
14 |
15 | First, run the development server:
16 |
17 | ```bash
18 | npm run dev
19 | # or
20 | yarn dev
21 | ```
22 |
23 | Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
24 |
25 | ## Scripts
26 |
27 | - `dev`: starts the application in development mode;
28 | - `build`: creates the production version;
29 | - `preview`: initializes a local static web server that serves files from the `dist` directory (this is an easy way to verify that the production build is working correctly);
30 | - `lint`: runs linter on all files that are in the configuration rule;
31 | - `lint:fix`: runs linter and fixes all files that are in the configuration rule;
32 | - `typecheck`: checks if the file types are correct;
33 | - `test`: runs unit and integration tests;
34 | - `test:watch`: runs unit and integration tests in listening mode;
35 | - `coverage-test`: runs unit and integration tests and generates a code coverage report;
36 | - `coverage-test:watch`: runs unit and integration tests and generates a code coverage report in listen mode;
37 | - `generate:component`: creates basic structure of a component;
38 | - `generate:hook`: creates the basic structure of a hook.
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-typescript-boilerplate",
3 | "private": true,
4 | "version": "1.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview",
10 | "lint": "eslint 'src/**/*.{ts,tsx}' --max-warnings=0",
11 | "lint:fix": "npm run lint -- --fix",
12 | "typecheck": "tsc --project tsconfig.json --noEmit",
13 | "test": "jest --maxWorkers=50%",
14 | "test:watch": "jest --watch --maxWorkers=25%",
15 | "test:ci": "jest --runInBand",
16 | "coverage-test": "jest --coverage --maxWorkers=50%",
17 | "coverage-test:watch": "jest --coverage --watch --maxWorkers=25%",
18 | "generate:component": "plop component --plopfile ./generators/plopfile.cjs",
19 | "generate:hook": "plop hook --plopfile ./generators/plopfile.cjs",
20 | "postinstall": "husky install",
21 | "prepare": "husky install"
22 | },
23 | "lint-staged": {
24 | "src/**/*": [
25 | "npm run lint"
26 | ]
27 | },
28 | "dependencies": {
29 | "react": "^18.3.1",
30 | "react-dom": "^18.3.1"
31 | },
32 | "devDependencies": {
33 | "@babel/preset-env": "^7.28.5",
34 | "@babel/preset-react": "^7.28.5",
35 | "@babel/preset-typescript": "^7.28.5",
36 | "@testing-library/jest-dom": "^5.17.0",
37 | "@testing-library/react": "^15.0.7",
38 | "@types/jest": "^30.0.0",
39 | "@types/react": "^18.3.12",
40 | "@types/react-dom": "^18.3.1",
41 | "@typescript-eslint/eslint-plugin": "^5.62.0",
42 | "@typescript-eslint/parser": "^5.62.0",
43 | "@vitejs/plugin-react": "^5.1.0",
44 | "eslint": "^8.57.1",
45 | "eslint-config-prettier": "^10.1.8",
46 | "eslint-plugin-prettier": "^5.5.4",
47 | "eslint-plugin-react": "^7.37.5",
48 | "eslint-plugin-react-hooks": "^7.0.1",
49 | "husky": "^9.1.7",
50 | "jest": "^30.2.0",
51 | "jest-environment-jsdom": "^30.2.0",
52 | "lint-staged": "^16.2.6",
53 | "plop": "^4.0.4",
54 | "prettier": "3.6.2",
55 | "typescript": "^5.9.3",
56 | "vite": "^7.1.12"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------