├── .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}}) 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 | --------------------------------------------------------------------------------