4. Creating an App

Chapter 4: Creating a React Application

Scaffolding a project is only the first step. To build scalable software, professional engineers must understand project configuration files, maintain modular folder architectures, manage environment variables securely, and execute optimized production builds.

1. Project Scaffolding & Initial Commands

We will initialize a clean, modern React project using Vite. Run the following command in your terminal:

# 1. Initialize project directory with React template
npm create vite@latest hifi-react-store -- --template react

# 2. Enter project directory
cd hifi-react-store

# 3. Install core dependencies
npm install

# 4. Start local development server
npm run dev

Your terminal will display a local URL (typically http://localhost:5173/). Any modifications made to your source files will immediately reflect in the browser without losing component state viaHot Module Replacement (HMR).

2. Dissecting Project Configuration Files

Let's examine the core files that orchestrate your project's dependencies, compilation, and behavior:

A. The `package.json` File

The manifest containing project metadata, executable scripts, and required dependencies:

{
  "name": "hifi-react-store",
  "private": true,
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",               // Starts instant local development server
    "build": "vite build",        // Transpiles, bundles, and minifies for production
    "lint": "eslint .",           // Scans codebase for code quality & React hook bugs
    "preview": "vite preview"     // Spreads production dist/ bundle locally for testing
  },
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },
  "devDependencies": {
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@vitejs/plugin-react": "^4.3.1",
    "eslint": "^9.9.0",
    "vite": "^5.4.1"
  }
}

B. The `vite.config.js` File

Controls the compilation pipeline. You can configure plugins, path aliases, and backend proxy tunnels here:

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

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      // Allows using '@/components/Button' instead of '../../components/Button'
      '@': path.resolve(__dirname, './src'),
    },
  },
  server: {
    port: 3000, // Customize dev server port
    proxy: {
      // Route API requests to a backend server to prevent CORS issues in development
      '/api': {
        target: 'http://localhost:5000',
        changeOrigin: true,
      },
    },
  },
});

3. Managing Environment Variables Securely

Never hardcode API endpoint URLs, payment keys, or configuration constants directly into your source code. Instead, store them in environment files:

File NamePurpose & Git Tracking
.envDefault variables loaded in all environments. Tracked in git unless containing secrets.
.env.developmentVariables loaded only during npm run dev (e.g., local mock API endpoints).
.env.productionVariables loaded during npm run build (e.g., production cloud API URLs).
.env.localLocal override file containing sensitive secrets. MUST be added to .gitignore!
⚠️ Security Warning: Client-Side Environment Exposure

Any environment variable prefixed with VITE_ or bundled into client-side JavaScript ispublicly readable by anyone who opens your website and inspects the bundle in browser DevTools.NEVER store private API secret keys, database passwords, or private signing tokens in frontend .env files!

Example usage in Vite:

// .env
VITE_API_BASE_URL=https://api.hifitoolkit.com/v1

// In your React Component (e.g., src/services/api.js):
const API_URL = import.meta.env.VITE_API_BASE_URL;

export async function fetchCatalog() {
  const response = await fetch(`${API_URL}/products`);
  return response.json();
}

4. Scalable Folder Architecture for Enterprise Projects

As applications expand past 50 components, flat structures become unmaintainable. The industry standard pattern organizes files by feature/domain alongside shared core utilities:

src/
├── assets/             # Global static media (images, fonts, vector icons)
├── components/         # Shared, domain-agnostic UI primitives (Button, Modal, Input, Navbar)
│   ├── Button/
│   │   ├── Button.jsx
│   │   ├── Button.module.css
│   │   └── Button.test.jsx
├── context/            # Global React Context providers (AuthContext, ThemeContext)
├── features/           # Domain-specific modules (User, Checkout, Products)
│   ├── auth/
│   │   ├── components/ # LoginForm.jsx, RegisterModal.jsx
│   │   ├── hooks/      # useAuth.js
│   │   └── services/   # authApi.js
│   └── cart/
│       ├── components/ # CartDrawer.jsx, CartItem.jsx
│       └── hooks/      # useCart.js
├── hooks/              # Global custom hooks (useDebounce, useMediaQuery, useLocalStorage)
├── layouts/            # Page shell wrappers (MainLayout.jsx, DashboardLayout.jsx)
├── pages/              # Routed view screens (HomePage.jsx, ProductDetailPage.jsx)
├── services/           # HTTP clients and base Axios / Fetch configurations
├── utils/              # Pure helper functions (formatCurrency.js, formatDate.js)
├── App.jsx             # Top-level root router and providers
└── main.jsx            # DOM mount entry point

5. Building for Production & Deployment Pipelines

When you are ready to ship your application to live users, run the build command:

npm run build

Vite executes the following automated pipeline:

  • Tree-Shaking: Removes unused library code and functions.
  • Minification: Strips whitespace, comments, and shortens variable names using Terser / esbuild.
  • Code-Splitting: Breaks large bundles into smaller asynchronous chunks loaded only when routes are accessed.
  • Asset Hashing: Appends content hashes to filenames (e.g., index-B1z9eA.js) to enable long-term browser cache invalidation.

Previewing the Production Bundle Locally:

npm run preview

Deploying to Modern Hosting Providers:

Hosting ProviderBuild CommandPublish / Output Directory
Vercelnpm run builddist
Netlifynpm run builddist
GitHub Pagesnpm run builddist
AWS S3 + CloudFrontnpm run builddist

Frequently Asked Questions (FAQ)

What is the difference between dependencies and devDependencies in package.json?

Dependencies are packages required for the application to run in production (e.g., react, react-dom, react-router-dom). DevDependencies are packages only needed during local development and build time (e.g., vite, eslint, prettier, typescript, test runners). DevDependencies are stripped away during the production build.

How do environment variables work in Vite compared to Create React App?

In Vite, environment variables in your .env file must be prefixed with 'VITE_' (e.g., VITE_API_URL=https://api.example.com) and are accessed in client code via 'import.meta.env.VITE_API_URL'. In legacy Create React App, they were prefixed with 'REACT_APP_' and accessed via 'process.env.REACT_APP_API_URL'.

What is tree-shaking during the production build?

Tree-shaking is a dead-code elimination technique performed by module bundlers (like Rollup in Vite or Webpack). Because ES Modules use static 'import' and 'export' syntax, the bundler analyzes your code and excludes any exported functions or modules that are never actually called, reducing production bundle size.

How do you test a production build locally before deploying?

After compiling your production bundle with 'npm run build', you can immediately spin up a local preview server matching production behavior by executing 'npm run preview' in Vite, or using the 'serve -s dist' utility.

Explore Related Tools

Hand-picked utilities and calculators related to this tool.

Related Tools