Skip to content

React Tutorial Modern React and Production

A Modern React Project Structure: Tooling and Scripts

Lesson 127 of 156 3 min read By Deepak Paswan Updated Oct 10, 2026

Full-project lesson · needs Node.js, npm and real project files, so it does not run in the in-page playground.

By now you have built components, hooks and routes. This lesson zooms out: what a maintainable, professional React project contains beyond the code, and how the pieces fit together.

The root of the project

code
PLAIN
my-app/
├── index.html              the single HTML page; has the root element
├── package.json            dependencies and scripts
├── package-lock.json       exact versions (commit it)
├── vite.config.ts          build and dev-server settings (and test settings)
├── tsconfig.json           TypeScript settings (references the two below)
├── tsconfig.app.json       settings for code in src
├── tsconfig.node.json      settings for config files such as vite.config.ts
├── .oxlintrc.json          lint rules (Oxlint in the Vite template)
├── .gitignore              node_modules, dist, *.local
├── .env                    shared public settings (VITE_ variables)
├── .env.example            documents which variables exist
├── README.md               how to run, test, build and deploy
├── public/                 files served as they are (favicon, robots.txt)
└── src/                    your code

This is the layout of the react-ts Vite template (create-vite 9, Vite 8, TypeScript 6.0 at the time of writing), plus the .env files and README you add yourself.

Inside src

A structure that scales (Feature-Based Project Structure):

code
PLAIN
src/
├── main.tsx          starts React
├── App.tsx           the app shell
├── app/              providers, router, global layout
├── features/         products/, cart/, account/ ... each with components, hooks, api
├── shared/           reusable components, hooks and utilities
├── test/             test setup files
└── index.css         global styles and CSS variables

The package.json scripts

Every project should answer the same four questions with a script:

config.json
JSON
{
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "oxlint",
    "preview": "vite preview",
    "test": "vitest"
  }
}
ScriptQuestion it answers
npm run devHow do I work on it?
npm run lintIs the code consistent and free of obvious mistakes?
npm testDoes it still work?
npm run buildDoes it compile and bundle for production? (In the TypeScript template this also type-checks.)
npm run previewWhat will the production build look like?

The first four lines come straight from the Vite react-ts template; test is the line you add when you set up testing (Basic Testing Concepts).

Tooling you will meet

  • Linter: finds bugs and enforces rules. The template includes Oxlint with React rules, including the Rules of Hooks. Many teams use ESLint with eslint-plugin-react-hooks instead; either is fine, but have one.
  • Formatter: ends style arguments. Prettier is the most common.
  • TypeScript: catches mistakes before running (React with TypeScript).
  • Test runner: Vitest with Testing Library is the usual match for Vite apps.
  • Git hooks (for example Husky or lint-staged): run lint and format on each commit.
  • Editor settings: share them with .editorconfig and .vscode recommendations.

Environment files

Keep configuration out of the code (Environment Variables). A common convention:

code
PLAIN
.env                shared defaults that are safe to publish
.env.example        a copy with fake values, committed, as documentation
.env.local          your own overrides and anything private (never committed)
.env.production     values used by production builds

Remember that every VITE_ value is public; keep real secrets on a server (Why Secret Keys Must Not Be Exposed).

Continuous integration

A CI service (GitHub Actions, GitLab CI and others) runs your checks on every push, so broken code is found early. The commands are the same ones you run locally:

terminal
BASH
npm ci          # install exactly what package-lock.json says (faster and stricter than npm install)
npm run lint
npm test -- --run
npm run build

npm test -- --run makes Vitest run once and exit instead of staying in watch mode. Add these steps to your CI configuration, and fail the build if any step fails.

Dependencies: keep them few and healthy

  • Add a package only when it saves real work. Each one is code you must trust and update.
  • Run npm outdated and npm audit regularly.
  • Commit the lock file, and update packages in small, tested steps.
  • Prefer widely used, maintained packages, and check that they support your React version (React 19).

The README

A new teammate (or you in six months) should be able to run the project from the README alone: what it is, how to install, the scripts, the environment variables and how to deploy. Keep it short and correct.

A checklist for a healthy project

  • [ ] npm install then npm run dev works on a fresh machine
  • [ ] Lint, tests and build run in CI
  • [ ] TypeScript is on (or JSDoc types in plain JavaScript)
  • [ ] .env.example documents every variable; no secrets are committed
  • [ ] The folder structure is documented and consistent
  • [ ] Error handling, loading states and accessibility are part of “done”

Key points

  • A modern project is more than components: config, scripts, linting, types, tests and CI.
  • Every project should have dev, lint, test, build and preview scripts.
  • Keep configuration in .env files, dependencies few, and the README accurate.