Setting Up a React Development Environment

Module 22: Web Frameworks I (JavaScript) - Wednesday: React Fundamentals

Modern React Development Environment

Setting up an efficient React development environment is crucial for productivity and code quality. Today's React development involves several tools and configurations that work together to provide a smooth development experience.

graph TD A[React Development Environment] --> B[Package Manager] A --> C[Build Tool] A --> D[Development Server] A --> E[Compiler/Transpiler] A --> F[Linting/Formatting] A --> G[Testing Framework] B --> B1[npm] B --> B2[yarn] B --> B3[pnpm] C --> C1[Webpack] C --> C2[Vite] C --> C3[Parcel] E --> E1[Babel] E --> E2[TypeScript] E --> E3[SWC] F --> F1[ESLint] F --> F2[Prettier] G --> G1[Jest] G --> G2[React Testing Library] G --> G3[Vitest] style A fill:#f9d71c,stroke:#333,stroke-width:2px

The Workshop Analogy

Think of a React development environment like a woodworking workshop:

  • Package Manager (npm/yarn/pnpm): Like the supply store where you get your materials and tools
  • Build Tools (Webpack/Vite): Like your workbench that holds everything together
  • Transpilers (Babel/TypeScript): Like planers and sanders that transform raw wood into usable pieces
  • Development Server: Like a showroom where you can view your work in progress
  • Linters (ESLint): Like measuring tools that ensure everything is straight and correct
  • Formatters (Prettier): Like finishing tools that make your work look polished
  • Testing Tools: Like quality control checks before delivery

Just as a well-organized workshop helps a carpenter work efficiently, a properly configured development environment helps React developers build applications more effectively.

Why a Development Environment Matters

A robust React development environment provides several key benefits:

Evolution of React Development Tooling

React development tooling has evolved significantly over the years:

Early Days (2013-2015)

  • Manual script tags and simple builds
  • Basic Browserify or Gulp configurations
  • JSX transformations with standalone tools
  • Limited hot-reloading capabilities

Webpack Era (2015-2020)

  • Complex Webpack configurations became standard
  • Create React App emerged to simplify setup
  • Babel gained sophisticated React plugins
  • Hot Module Replacement improved developer experience
  • ESLint with React plugins became common

Modern Era (2020-Present)

  • New build tools like Vite deliver 10-100x faster development builds
  • TypeScript adoption has become mainstream
  • React frameworks like Next.js and Remix provide integrated experiences
  • Testing practices have matured with React Testing Library
  • Integrated solutions manage more of the toolchain automatically

Companies like Facebook, Airbnb, and Shopify invest heavily in their React development environments to maximize developer productivity. Many have dedicated teams that maintain custom tooling tailored to their specific needs.

Using Create React App

Create React App (CRA) remains one of the most popular ways to set up a new React project with minimal configuration. It's an officially supported way to create single-page React applications with a robust development environment.

Features of Create React App

Creating a New React App with CRA


# Using npx (recommended)
npx create-react-app my-app

# Using npm
npm init react-app my-app

# Using yarn
yarn create react-app my-app

# With TypeScript template
npx create-react-app my-app --template typescript

# With specific package manager
npx create-react-app my-app --use-npm
# or
npx create-react-app my-app --use-pnpm
                

After running these commands, CRA will set up a new project with all the necessary dependencies and configuration. It will also initialize a Git repository and perform an initial commit.

CRA Project Structure


my-app/
├── README.md               # Project documentation
├── node_modules/           # Installed packages
├── package.json            # Dependencies and scripts
├── .gitignore              # Git ignore file
├── public/                 # Static files
│   ├── favicon.ico         # Favicon file
│   ├── index.html          # HTML template
│   ├── logo192.png         # Small logo
│   ├── logo512.png         # Large logo
│   ├── manifest.json       # PWA manifest
│   └── robots.txt          # Robots file
└── src/                    # Source code
    ├── App.css             # App component styles
    ├── App.js              # Main App component
    ├── App.test.js         # App component test
    ├── index.css           # Global styles
    ├── index.js            # Entry point
    ├── logo.svg            # React logo
    ├── reportWebVitals.js  # Performance monitoring
    └── setupTests.js       # Test configuration
                

This structure provides a good starting point for React applications. The src directory is where you'll spend most of your time, adding components, styles, and other application code.

Available Scripts


# Start development server
npm start
# or
yarn start

# Run tests
npm test
# or
yarn test

# Build for production
npm run build
# or
yarn build

# Eject from CRA
npm run eject
# or
yarn eject
                

Create React App provides these built-in scripts to handle common development tasks. The "eject" command is a one-way operation that exposes all the configuration files and dependencies, giving you full control but removing the ability to receive CRA updates.

Customizing the Environment


# Create .env file in the project root
touch .env

# Add environment variables (must start with REACT_APP_)
# .env file content:
REACT_APP_API_URL=https://api.example.com
REACT_APP_FEATURE_FLAG=true
REACT_APP_VERSION=$npm_package_version

# Using environment variables in your code
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <p>API URL: {process.env.REACT_APP_API_URL}</p>
        <p>Version: {process.env.REACT_APP_VERSION}</p>
        {process.env.REACT_APP_FEATURE_FLAG === 'true' && (
          <p>🎉 New feature is enabled!</p>
        )}
      </header>
    </div>
  );
}
                

Environment variables provide a way to customize your application for different environments (development, staging, production) without changing the code. In Create React App, environment variables must start with REACT_APP_ to be included in the bundle.

When to Use Create React App

Create React App is well-suited for many projects, but it's important to understand when it's the right choice:

Good Use Cases for CRA

  • Learning React: Ideal for beginners getting started with React
  • Single-Page Applications: Well-designed for SPAs with client-side routing
  • Prototypes and MVPs: Quick to set up with minimal overhead
  • Small to Medium Projects: Provides all essential tools without complexity
  • Internal Tools: Great for dashboards and admin interfaces

When to Consider Alternatives

  • Server-Side Rendering Needed: Consider Next.js or Remix instead
  • Static Site Generation: Gatsby or Next.js might be better options
  • Highly Custom Builds: If you need extensive webpack customization
  • Performance-Critical Applications: Newer tools like Vite may offer better performance
  • Micro-Frontends: May require more specialized setups

Companies often start with Create React App and later migrate to more specialized solutions as their needs evolve. For example, Airbnb began with CRA but eventually created their own build configuration to support their specific requirements.

Modern Alternatives to Create React App

While Create React App has been the go-to solution for years, several modern alternatives have emerged that offer different advantages. Let's explore some of the most popular options.

Vite

Vite (French for "quick") is a build tool created by Evan You, the creator of Vue.js. It has gained significant popularity in the React community due to its exceptional speed and developer experience.

Setting Up a React Project with Vite


# Using npm
npm create vite@latest my-react-app -- --template react

# Using yarn
yarn create vite my-react-app --template react

# Using pnpm
pnpm create vite my-react-app --template react

# With TypeScript
npm create vite@latest my-react-app -- --template react-ts
                

After creation, you'll need to install dependencies and start the development server:


cd my-react-app
npm install  # or yarn or pnpm install
npm run dev  # or yarn dev or pnpm dev
                

Vite Project Structure


my-react-app/
├── README.md               # Project documentation
├── node_modules/           # Installed packages
├── package.json            # Dependencies and scripts
├── vite.config.js          # Vite configuration
├── index.html              # HTML template (at root level)
├── public/                 # Static files
│   └── vite.svg            # Vite logo
└── src/                    # Source code
    ├── App.css             # App component styles
    ├── App.jsx             # Main App component
    ├── index.css           # Global styles
    ├── main.jsx            # Entry point
    └── assets/             # Static assets
        └── react.svg       # React logo
                

Unlike CRA, Vite places the HTML template at the root level and treats it as the entry point. This reflects Vite's approach of leveraging native ES modules during development.

Why Choose Vite?

Next.js

Next.js is a full-featured React framework that provides server-side rendering, static site generation, and other advanced features out of the box.

Setting Up a Next.js Project


# Using npx
npx create-next-app@latest my-next-app

# Using yarn
yarn create next-app my-next-app

# Using pnpm
pnpm create next-app my-next-app

# With TypeScript
npx create-next-app@latest my-next-app --typescript
                

After creation, you can start the development server:


cd my-next-app
npm run dev  # or yarn dev or pnpm dev
                

Next.js Project Structure


my-next-app/
├── README.md               # Project documentation
├── node_modules/           # Installed packages
├── package.json            # Dependencies and scripts
├── .eslintrc.json          # ESLint configuration
├── next.config.js          # Next.js configuration
├── public/                 # Static files
│   ├── favicon.ico         # Favicon file
│   └── vercel.svg          # Vercel logo
└── pages/                  # File-based routing
    ├── _app.js             # Custom App component
    ├── _document.js        # Custom Document component
    ├── index.js            # Home page
    └── api/                # API routes
        └── hello.js        # Example API endpoint
└── styles/                 # CSS styles
    ├── globals.css         # Global styles
    └── Home.module.css     # CSS modules
                

Next.js uses a file-based routing system where files in the pages directory automatically become routes. This is a key difference from CRA and Vite.

Why Choose Next.js?

Remix

Remix is a newer React framework that focuses on web fundamentals and progressive enhancement. It was created by the team behind React Router.

Setting Up a Remix Project


# Using npx
npx create-remix@latest my-remix-app

# When prompted, choose:
# - Just the basics
# - Remix App Server
# - TypeScript or JavaScript
# - Run npm install

# Start the development server
cd my-remix-app
npm run dev
                

Remix will guide you through a few decisions during setup, allowing you to customize your starting point.

Remix Project Structure


my-remix-app/
├── README.md               # Project documentation
├── node_modules/           # Installed packages
├── package.json            # Dependencies and scripts
├── remix.config.js         # Remix configuration
├── app/                    # Application code
│   ├── entry.client.tsx    # Client entry point
│   ├── entry.server.tsx    # Server entry point
│   ├── root.tsx            # Root component
│   ├── routes/             # Route components
│   │   ├── index.tsx       # Home page
│   │   └── about.tsx       # About page
│   └── styles/             # CSS styles
│       └── global.css      # Global styles
├── public/                 # Static files
│   └── favicon.ico         # Favicon file
└── server.js               # Server entry point
                

Remix uses a nested route structure in the app/routes directory, which is different from both CRA and Next.js approaches.

Why Choose Remix?

Comparison of Modern React Setups

Feature Create React App Vite Next.js Remix
Development Speed Moderate Very Fast Fast Fast
SSR Support No No (needs plugins) Yes Yes
Routing Manual (React Router) Manual (React Router) File-based File-based (nested)
API Routes No No Yes Yes (loader/action)
Bundle Size Larger Smaller Optimized Optimized
Configuration Hidden (Eject needed) Minimal, transparent Moderate Moderate
Learning Curve Low Low Moderate Moderate-High
Ideal For SPAs, Learning SPAs, Fast Development SEO, Production Apps UX-focused Apps

Companies often choose different tools based on their specific needs:

  • Shopify: Uses Next.js for many customer-facing pages to optimize SEO
  • Netflix: Uses custom CRA-like setups for their internal dashboards
  • Airbnb: Uses a custom build system with SSR capabilities
  • Meta: Uses custom internal tools but contributes to create-react-app

Many companies are migrating from Create React App to Vite for faster development experience or to Next.js for better SEO and performance.

Development Environment Customization

Regardless of which tool you choose to start your React project, you'll likely want to customize your development environment to suit your team's needs and preferences.

Code Quality Tools

Ensuring code quality is essential for maintainable React applications. Here are some essential tools:

ESLint Configuration


# Install ESLint with React support
npm install --save-dev eslint eslint-plugin-react eslint-plugin-react-hooks

# Create ESLint configuration file
# .eslintrc.js
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
  ],
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: ['react', 'react-hooks'],
  rules: {
    // Custom rules
    'react/prop-types': 'warn',
    'react/react-in-jsx-scope': 'off', // Not needed in React 17+
    'react-hooks/rules-of-hooks': 'error',
    'react-hooks/exhaustive-deps': 'warn',
    'no-unused-vars': 'warn',
  },
  settings: {
    react: {
      version: 'detect',
    },
  },
};
                

This configuration enforces React best practices, including the Rules of Hooks. You can adjust the rules to match your team's preferences.

Prettier Configuration


# Install Prettier
npm install --save-dev prettier

# Create Prettier configuration file
# .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 80,
  "bracketSpacing": true,
  "jsxBracketSameLine": false,
  "arrowParens": "avoid"
}

# Create .prettierignore file
# .prettierignore
build
dist
node_modules
coverage
                

Prettier ensures consistent code formatting across your team, reducing code review discussions about style.

Integrating ESLint with Prettier


# Install integration packages
npm install --save-dev eslint-config-prettier eslint-plugin-prettier

# Update ESLint configuration
# .eslintrc.js
module.exports = {
  // Previous config...
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'plugin:prettier/recommended', // Add this line
  ],
  // Rest of config...
};
                

This integration ensures that ESLint and Prettier work together without conflicts.

TypeScript Integration

Adding TypeScript to your React project provides type safety and better IDE support.

Adding TypeScript to an Existing Project


# Install TypeScript and React types
npm install --save-dev typescript @types/react @types/react-dom @types/node

# Create a basic tsconfig.json file
# tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["src"]
}
                

After setting up TypeScript, you can gradually convert your .js/.jsx files to .ts/.tsx files.

Converting a Component to TypeScript


// Before: UserProfile.jsx
import React from 'react';

function UserProfile({ name, age, roles }) {
  return (
    <div className="user-profile">
      <h2>{name}, {age}</h2>
      <ul>
        {roles.map(role => (
          <li key={role}>{role}</li>
        ))}
      </ul>
    </div>
  );
}

export default UserProfile;

// After: UserProfile.tsx
import React from 'react';

interface UserProfileProps {
  name: string;
  age: number;
  roles: string[];
  isActive?: boolean; // Optional prop
}

function UserProfile({ name, age, roles, isActive = true }: UserProfileProps) {
  return (
    <div className={`user-profile ${isActive ? 'active' : 'inactive'}`}>
      <h2>{name}, {age}</h2>
      <ul>
        {roles.map(role => (
          <li key={role}>{role}</li>
        ))}
      </ul>
    </div>
  );
}

export default UserProfile;
                

TypeScript provides clear documentation of the component's API through interfaces, making it easier for other developers to use your components correctly.

Customizing Build Configuration

Sometimes you need to customize the build configuration to add features or optimize performance.

Create React App Configuration


# Using CRACO (Create React App Configuration Override)
npm install --save-dev @craco/craco

# Create craco.config.js in the project root
# craco.config.js
const path = require('path');

module.exports = {
  webpack: {
    alias: {
      '@components': path.resolve(__dirname, 'src/components'),
      '@hooks': path.resolve(__dirname, 'src/hooks'),
      '@utils': path.resolve(__dirname, 'src/utils'),
      '@styles': path.resolve(__dirname, 'src/styles'),
    },
    plugins: [
      // Add custom webpack plugins here
    ],
  },
  style: {
    postcss: {
      plugins: [
        require('tailwindcss'),
        require('autoprefixer'),
      ],
    },
  },
};

# Update package.json scripts
"scripts": {
  "start": "craco start",
  "build": "craco build",
  "test": "craco test",
  "eject": "react-scripts eject"
}
                

CRACO allows you to customize the webpack configuration without ejecting from Create React App.

Vite Configuration


# vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@components': path.resolve(__dirname, './src/components'),
      '@hooks': path.resolve(__dirname, './src/hooks'),
      '@utils': path.resolve(__dirname, './src/utils'),
    },
  },
  server: {
    port: 3000,
    open: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },
  build: {
    outDir: 'build',
    sourcemap: true,
    chunkSizeWarningLimit: 1600,
  },
});
                

Vite has a more straightforward configuration system that's easier to customize.

Next.js Configuration


// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    domains: ['images.example.com', 'cdn.someservice.com'],
  },
  async redirects() {
    return [
      {
        source: '/old-page',
        destination: '/new-page',
        permanent: true,
      },
    ];
  },
  async rewrites() {
    return [
      {
        source: '/api/:path*',
        destination: 'https://api.example.com/:path*',
      },
    ];
  },
  env: {
    NEXT_PUBLIC_ANALYTICS_ID: 'your-analytics-id',
  },
}

module.exports = nextConfig
                

Next.js has its own configuration system that provides features specific to its architecture, such as image optimization, redirects, and rewrites.

Environment Customization in Practice

In professional React projects, development environments often include additional customizations:

Git Hooks with Husky


# Install Husky and lint-staged
npm install --save-dev husky lint-staged

# Setup Husky
npx husky install
npm set-script prepare "husky install"

# Add a pre-commit hook
npx husky add .husky/pre-commit "npx lint-staged"

# Configure lint-staged in package.json
"lint-staged": {
  "*.{js,jsx,ts,tsx}": [
    "eslint --fix",
    "prettier --write"
  ],
  "*.{json,css,md}": [
    "prettier --write"
  ]
}
                

This setup ensures that all code is linted and formatted before being committed, maintaining code quality standards.

Custom Development Proxies

Real-world applications often need to communicate with APIs during development:


// For Create React App, in package.json:
"proxy": "http://localhost:8080"

// For more complex scenarios, create a setupProxy.js file:
// src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:8080',
      changeOrigin: true,
    })
  );
  
  app.use(
    '/auth',
    createProxyMiddleware({
      target: 'http://localhost:8081',
      changeOrigin: true,
    })
  );
};
                

Testing Configuration

Comprehensive testing setups for React applications:


// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
    '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
  },
  transform: {
    '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
  },
  coverageThreshold: {
    global: {
      branches: 70,
      functions: 70,
      lines: 70,
      statements: 70,
    },
  },
};

// src/setupTests.js
import '@testing-library/jest-dom';
                

Companies like Airbnb, Shopify, and Netflix invest significant effort in customizing their development environments to maximize developer productivity and code quality. These customizations often evolve over time as the team learns what works best for their specific needs.

Developer Tools and Extensions

A powerful development environment extends beyond the project setup to include browser tools and IDE extensions that enhance productivity.

React Developer Tools

React Developer Tools is a browser extension that allows you to inspect the React component tree and the props and state of your components.

graph TD A[React Developer Tools] --> B[Components Tab] A --> C[Profiler Tab] B --> D[Component Tree] B --> E[Props and State] B --> F[Hooks] C --> G[Performance Measurement] C --> H[Render Timing] C --> I[Commit Analysis] style A fill:#a1c2ff,stroke:#333,stroke-width:2px

React Developer Tools is available for:

Key Features:

VS Code Extensions for React Development

Visual Studio Code has become the most popular editor for React development due to its extensive extension ecosystem.

Essential VS Code Extensions:

TypeScript-Specific Extensions:

Browser Extensions for Web Development

Professional Development Workflows

Experienced React developers typically set up their development environment to maximize productivity:

IDE Setup

  • Custom VS Code Settings: Teams often share a standardized .vscode/settings.json file in their repositories
  • Snippets Library: Custom snippets for company-specific patterns
  • Custom Key Bindings: Shortcuts for common React operations

Example VS Code Settings for React Projects


// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "typescript.tsdk": "node_modules/typescript/lib",
  "javascript.updateImportsOnFileMove.enabled": "always",
  "typescript.updateImportsOnFileMove.enabled": "always",
  "emmet.includeLanguages": {
    "javascript": "javascriptreact",
    "typescript": "typescriptreact"
  },
  "workbench.colorCustomizations": {
    "activityBar.background": "#282c34",
    "titleBar.activeBackground": "#282c34",
    "titleBar.activeForeground": "#61dafb"
  }
}
                

Terminal and Git Integration

Many developers integrate terminals and Git directly into their workflow:

  • VS Code Integrated Terminal: Running development servers and commands without switching context
  • Git Lens: View file history and blame information inline
  • GitHub Pull Requests: Review and manage PRs from within VS Code

Component Development

Tools that assist in developing and testing components:

  • Storybook: Develop and test UI components in isolation
  • Bit.dev: Share and reuse components across projects
  • Figma/Sketch Integration: Plugins that connect design tools to code

Companies like Airbnb provide comprehensive onboarding documentation for their preferred development environment setup, ensuring that all developers have a consistent and productive experience.

Practical Exercise: Setting Up Your React Environment

Let's put everything we've learned into practice by setting up a complete React development environment.

Complete React Environment Setup

Objective: Create a modern React development environment with TypeScript, ESLint, Prettier, and testing tools.

Requirements:

  1. Choose and set up a React project using one of the tools we discussed (CRA, Vite, or Next.js)
  2. Configure TypeScript for type safety
  3. Set up ESLint with React-specific rules
  4. Configure Prettier for consistent formatting
  5. Add Git hooks with Husky and lint-staged
  6. Configure path aliases for better imports
  7. Set up a basic testing environment
  8. Create a simple React component to verify the setup

Step-by-Step Instructions:

1. Project Setup


# Choose one of these options:

# Option A: Create React App with TypeScript
npx create-react-app my-react-app --template typescript

# Option B: Vite with TypeScript
npm create vite@latest my-react-app -- --template react-ts

# Option C: Next.js with TypeScript
npx create-next-app@latest my-react-app --typescript

# Navigate to the project directory
cd my-react-app
                

2. ESLint and Prettier Setup


# Install ESLint and Prettier dependencies
npm install --save-dev eslint-config-prettier eslint-plugin-prettier prettier

# Create .prettierrc file
echo '{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5"
}' > .prettierrc

# Create .prettierignore file
echo 'build
dist
node_modules
coverage
.next
out' > .prettierignore
                

3. ESLint Configuration

Create or update .eslintrc.js:


module.exports = {
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier'
  ],
  plugins: ['react', 'react-hooks', '@typescript-eslint', 'prettier'],
  rules: {
    'prettier/prettier': 'error',
    'react/react-in-jsx-scope': 'off',
    'react/prop-types': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off'
  },
  settings: {
    react: {
      version: 'detect'
    }
  },
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true
    },
    ecmaVersion: 2021,
    sourceType: 'module'
  },
  env: {
    browser: true,
    es2021: true,
    node: true
  }
};
                

4. Git Hooks Setup


# Install Husky and lint-staged
npm install --save-dev husky lint-staged

# Setup Husky
npx husky install
npm set-script prepare "husky install"
npx husky add .husky/pre-commit "npx lint-staged"

# Configure lint-staged in package.json
# Add this to your package.json:
"lint-staged": {
  "*.{js,jsx,ts,tsx}": [
    "eslint --fix",
    "prettier --write"
  ],
  "*.{json,css,md}": [
    "prettier --write"
  ]
}
                

5. Path Aliases Setup

For TypeScript, update tsconfig.json:


// Add to compilerOptions in tsconfig.json
"baseUrl": ".",
"paths": {
  "@/*": ["src/*"],
  "@components/*": ["src/components/*"],
  "@hooks/*": ["src/hooks/*"],
  "@utils/*": ["src/utils/*"],
  "@styles/*": ["src/styles/*"]
}
                

For Vite, update vite.config.ts:


import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@components': path.resolve(__dirname, './src/components'),
      '@hooks': path.resolve(__dirname, './src/hooks'),
      '@utils': path.resolve(__dirname, './src/utils'),
      '@styles': path.resolve(__dirname, './src/styles')
    }
  }
});
                

6. Create Project Structure


# Create directory structure
mkdir -p src/components/ui
mkdir -p src/hooks
mkdir -p src/utils
mkdir -p src/styles
mkdir -p src/pages  # For Next.js or client-side routing
                

7. Create a Sample Component

Create src/components/ui/Button.tsx:


import React from 'react';

interface ButtonProps {
  children: React.ReactNode;
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'small' | 'medium' | 'large';
  onClick?: () => void;
  disabled?: boolean;
}

const Button: React.FC<ButtonProps> = ({
  children,
  variant = 'primary',
  size = 'medium',
  onClick,
  disabled = false
}) => {
  const baseClasses = 'rounded font-semibold transition-colors';
  
  const variantClasses = {
    primary: 'bg-blue-500 text-white hover:bg-blue-600',
    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
    danger: 'bg-red-500 text-white hover:bg-red-600'
  };
  
  const sizeClasses = {
    small: 'px-3 py-1 text-sm',
    medium: 'px-4 py-2',
    large: 'px-6 py-3 text-lg'
  };
  
  const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : '';
  
  const classes = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${disabledClasses}`;
  
  return (
    <button
      className={classes}
      onClick={onClick}
      disabled={disabled}
      type="button"
    >
      {children}
    </button>
  );
};

export default Button;
                

8. Add a Simple Test

Create src/components/ui/Button.test.tsx:


import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';

describe('Button component', () => {
  test('renders button with children', () => {
    render(<Button>Click me</Button>);
    expect(screen.getByText('Click me')).toBeInTheDocument();
  });
  
  test('calls onClick handler when clicked', () => {
    const handleClick = jest.fn();
    render(<Button onClick={handleClick}>Click me</Button>);
    
    fireEvent.click(screen.getByText('Click me'));
    expect(handleClick).toHaveBeenCalledTimes(1);
  });
  
  test('is disabled when disabled prop is true', () => {
    render(<Button disabled>Click me</Button>);
    expect(screen.getByText('Click me')).toBeDisabled();
  });
});
                

9. Use the Component in Your App

Update src/App.tsx to use your new component:


import React, { useState } from 'react';
import Button from '@components/ui/Button';

function App() {
  const [count, setCount] = useState(0);
  
  return (
    <div className="App">
      <header className="App-header">
        <h1>React Environment Setup</h1>
        <p>Count: {count}</p>
        
        <div className="flex gap-2">
          <Button 
            variant="primary" 
            onClick={() => setCount(count + 1)}
          >
            Increment
          </Button>
          
          <Button 
            variant="secondary" 
            onClick={() => setCount(count - 1)}
            disabled={count === 0}
          >
            Decrement
          </Button>
          
          <Button 
            variant="danger" 
            onClick={() => setCount(0)}
            size="small"
          >
            Reset
          </Button>
        </div>
      </header>
    </div>
  );
}

export default App;
                

10. Run the Application


# Start the development server
npm start  # for CRA
# or
npm run dev  # for Vite or Next.js

# Run tests
npm test

# Check linting
npx eslint src --ext .ts,.tsx
                

Bonus Challenge: Add Tailwind CSS to your project for styling, or integrate Storybook for component development and documentation.

Further Resources