| name | react-project-structure |
| description | React 18+ project structure and scaffolding templates for migrated Angular applications. Use when creating new React projects (Vite or Next.js), setting up folder structure, or generating boilerplate for a TypeScript React SPA. |
React Project Structure Guide
Use this skill when setting up a React 18+ project (Vite SPA or Next.js) for a migrated Angular application.
Choosing Between Vite and Next.js
| Criteria | Vite + React (SPA) | Next.js (App Router) |
|---|
| Best for | Client-side SPA that consumes a REST API (mirrors original Angular shape) | Sites needing SSR / SSG / server components / SEO |
| Migration effort from Angular | Lower — 1:1 component & route mapping | Higher — server/client component split to design |
| Hosting on Azure | Azure Static Web Apps (ideal), Blob + CDN, or App Service | Azure App Service (Node runtime) or Container Apps |
| Bundle size / startup | Fast dev + smallest client output | Larger; framework overhead but hydration is optimized |
| Data fetching | TanStack Query / RTK Query on client | Server Components + fetch() with caching, or client-side query |
| SEO | Client-side only (needs prerender for SEO) | First-class SSR/SSG |
| Routing | react-router-dom v6 (explicit) | File-based (implicit) |
Recommendation: If the Angular source is a client-side SPA (uses HttpClient + Router, no SSR), pick Vite. Choose Next.js only if you need SSR/SSG or intend to add server-side rendering.
Vite Project Structure (Recommended default)
my-app/
├── package.json
├── tsconfig.json # App tsconfig (compilerOptions: strict, jsx: react-jsx)
├── tsconfig.node.json # tsconfig for vite.config.ts itself
├── vite.config.ts # Vite config + proxy for /api during dev
├── index.html # HTML entry (Vite serves this)
├── .env.development
├── .env.production
├── src/
│ ├── main.tsx # ReactDOM.createRoot(...).render(<App />)
│ ├── App.tsx # Root component + router setup
│ ├── routes/ # Route-level components (page equivalents)
│ │ ├── EmployeeListPage.tsx
│ │ ├── EmployeeFormPage.tsx
│ │ ├── DepartmentListPage.tsx
│ │ └── DepartmentFormPage.tsx
│ ├── components/ # Reusable/presentational components
│ │ ├── NavBar.tsx
│ │ ├── EmployeeTable.tsx
│ │ └── FormField.tsx
│ ├── hooks/ # Custom hooks (data + logic)
│ │ ├── useEmployees.ts
│ │ └── useDepartments.ts
│ ├── services/ # Pure API modules (axios calls, no React)
│ │ ├── api-client.ts
│ │ ├── employees.ts
│ │ └── departments.ts
│ ├── models/ # TypeScript types (mirrors Angular models)
│ │ ├── employee.ts
│ │ └── department.ts
│ ├── schemas/ # zod schemas (form validation)
│ │ ├── employee.schema.ts
│ │ └── department.schema.ts
│ ├── context/ # React Context providers (auth, theme, i18n)
│ │ └── AuthContext.tsx
│ ├── store/ # Redux Toolkit slices (if chosen over Context)
│ │ ├── index.ts
│ │ └── slices/
│ ├── utils/ # Framework-agnostic helpers
│ │ ├── formatters.ts # Intl / date-fns replacements for pipes
│ │ └── validators.ts
│ ├── styles/
│ │ ├── globals.css # Bootstrap import + app-wide styles
│ │ └── variables.css
│ └── test/ # Vitest setup + shared test utils
│ ├── setup.ts
│ └── test-utils.tsx # renderWithProviders(...) helper
├── public/ # Static assets served as-is
└── vitest.config.ts # (or config inside vite.config.ts)
Next.js Project Structure (App Router)
my-app/
├── package.json
├── tsconfig.json
├── next.config.js # Rewrites for /api → backend (dev proxy alternative)
├── .env.development
├── .env.production
├── app/ # File-based routing (App Router)
│ ├── layout.tsx # Root layout (Bootstrap import, providers)
│ ├── page.tsx # Redirect to /employees
│ ├── providers.tsx # Client-side Providers (QueryClientProvider, etc.)
│ ├── globals.css
│ ├── employees/
│ │ ├── page.tsx # /employees — list
│ │ ├── new/
│ │ │ └── page.tsx # /employees/new
│ │ └── edit/
│ │ └── [id]/
│ │ └── page.tsx # /employees/edit/:id
│ └── departments/
│ ├── page.tsx
│ ├── new/
│ │ └── page.tsx
│ └── edit/
│ └── [id]/
│ └── page.tsx
├── components/ # Reusable components
│ ├── NavBar.tsx
│ └── EmployeeTable.tsx
├── hooks/
│ └── useEmployees.ts
├── services/ # API modules
│ ├── api-client.ts
│ └── employees.ts
├── models/
├── schemas/
├── lib/ # Server-only utilities (secrets, DB, etc.)
└── public/
Note: In Next.js App Router, add 'use client'; at the top of any file that uses hooks, effects, event handlers, or browser-only APIs.
Template Files
See the templates directory for starter files:
Angular → React Folder Mapping
| Angular | React (Vite) | React (Next.js) |
|---|
src/main.ts | src/main.tsx | Framework-managed (no equivalent) |
src/index.html | index.html (project root) | app/layout.tsx (metadata) + generated HTML |
src/app/app.module.ts | Not needed (composed via imports) | Not needed |
src/app/app-routing.module.ts | src/App.tsx <Routes> block | app/**/page.tsx file structure |
src/app/app.component.ts | src/App.tsx | app/layout.tsx |
src/app/components/*/ (each folder) | src/components/ (flat) + src/routes/ (page-level) | app/**/page.tsx for pages, components/ for shared |
src/app/services/*.service.ts | src/services/*.ts (API) + src/hooks/use*.ts (hooks) | Same split |
src/app/models/*.model.ts | src/models/*.ts | models/*.ts |
src/environments/environment.ts | .env.development + import.meta.env | .env.development + process.env.NEXT_PUBLIC_* |
proxy.conf.json | vite.config.ts server.proxy | next.config.js rewrites() |
angular.json | vite.config.ts | next.config.js |
karma.conf.js + *.spec.ts | vitest.config.ts + *.test.tsx | vitest.config.ts + *.test.tsx |
styles.css | src/styles/globals.css | app/globals.css |
polyfills.ts | Handled by Vite (rarely needed) | Handled by Next.js |
assets/ | public/ | public/ |
React Naming Conventions
| Concept | Angular | React |
|---|
| Component files | kebab-case.component.ts (+ .html, .css) | PascalCase.tsx (single file with JSX + CSS import) |
| Component class/function | PascalCaseComponent | PascalCase |
| Service files | kebab-case.service.ts | camelCase.ts (employees.ts) or use*.ts (hooks) |
| Model / type files | kebab-case.model.ts | camelCase.ts (employee.ts) |
| Hook files | N/A | useCamelCase.ts (must start with use) |
| Route folders (Next.js) | N/A | kebab-case/ or [param]/ |
| Test files | *.spec.ts | *.test.tsx or *.spec.tsx (config-driven) |
| CSS Modules | N/A | Component.module.css (imported as styles.classname) |
| Constants | UPPER_SNAKE_CASE | UPPER_SNAKE_CASE |
| Props interface | N/A | ComponentNameProps (exported when reused) |
Best Practices
- Function components + hooks only — Don't write class components. Class components have no advantage in React 18+.
- Constructor-style DI → hooks — Every
constructor(private svc: Svc) becomes either an import or useSvc() custom hook.
- Split API from hooks — API modules stay framework-agnostic (
services/employees.ts), hooks wrap them (hooks/useEmployees.ts). Improves testability.
- TanStack Query for server state,
useState/Context for UI state, Redux Toolkit only when justified — Don't put server response data in Redux; TanStack Query already caches it.
- Types over interfaces for props, interfaces for public API contracts — either works; pick one and be consistent.
import.meta.env.VITE_* (Vite) / process.env.NEXT_PUBLIC_* (Next.js) — Only these prefixes are exposed to the client bundle.
key prop on every list — Never omit it. Use a stable ID, not the array index.
- Co-locate tests —
EmployeeList.test.tsx next to EmployeeList.tsx.
- Strict TypeScript from day 1 —
"strict": true in tsconfig.json. It catches Angular→React translation bugs (e.g., forgotten undefined cases).
- One default library set — Don't ship both TanStack Query and RTK Query, or both zustand and Redux Toolkit. Pick one per concern.
Key Differences from Angular Mindset
| Angular Convention | React Convention |
|---|
NgModule declares components and imports | Components are imported directly where used |
Templates in separate .html file | JSX inline in .tsx |
Two-way binding ([(ngModel)]) | Unidirectional data flow (value + onChange) |
providedIn: 'root' service singletons | Modules are singletons; export instances or wrap in Context |
Change detection zones (zone.js) | Explicit state updates trigger re-renders |
Structural directives (*ngIf, *ngFor) | JavaScript expressions inside JSX |
| Pipes for transformations in template | Regular JS functions called in JSX |
| Dependency Injection tokens | Import + parameters + Context |