Part 1.1 — Setting Up a React + TypeScript Project with pnpm
If you’ve followed Part 0, your environment is ready: Node, pnpm, PostgreSQL, and Git.
Now, we’ll build the frontend foundation — a modern React + TypeScript project using Vite as our build tool.
This setup is lightweight, type-safe, and built to scale with our NestJS backend later.
1. Why Use pnpm + Vite for React Projects?
Traditional npm installs are slow and redundant.
pnpm fixes that by creating a single global package store. It’s faster, space-efficient, and ideal for full-stack monorepos.
Vite, on the other hand, replaces old bundlers (like CRA or Webpack) with lightning-fast hot reloads and TypeScript support out of the box.
Together, they give you:
Faster builds
Zero config TypeScript support
Modern ES module compatibility
Efficient monorepo integration
2. Create the Project
Let’s generate a React + TypeScript app using Vite and pnpm:
# From your workspace root (e.g., fullstack-app/apps)
cd apps
pnpm create vite web --template react-ts
cd webNow install dependencies:
pnpm installThis creates a React app in /apps/web with TypeScript, ESLint, and hot module reload ready.
3. Folder Structure Overview
After Vite’s setup, your directory should look like this:
web/
├── public/
│ └── vite.svg
├── src/
│ ├── assets/
│ ├── App.tsx
│ ├── main.tsx
│ └── index.css
├── tsconfig.json
├── index.html
├── package.json
└── pnpm-lock.yamlWe’ll later adjust this structure for larger apps — separating components, hooks, and pages — but for now, keep it simple.
4. Configure ESLint and Prettier
Your editor should enforce code consistency automatically.
Let’s add ESLint and Prettier for linting and formatting.
pnpm add -D eslint prettier eslint-config-prettier eslint-plugin-react @typescript-eslint/parser @typescript-eslint/eslint-pluginCreate a .eslintrc.cjs:
module.exports = {
parser: "@typescript-eslint/parser",
parserOptions: { ecmaVersion: 2020, sourceType: "module" },
settings: { react: { version: "detect" } },
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
plugins: ["react", "@typescript-eslint"],
rules: {
"react/react-in-jsx-scope": "off"
}
};Add Prettier config (.prettierrc):
{
"singleQuote": true,
"semi": true,
"printWidth": 80
}Now test your setup:
pnpm dlx eslint src --fix5. Verify TypeScript Configuration
Open your tsconfig.json.
Ensure strict mode is enabled — it’ll make your app more reliable.
{
"compilerOptions": {
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}This ensures strong type checking, JSX support, and compatibility with modern React.
6. Run the App
Spin it up:
pnpm devOpen your browser at http://localhost:5173.
You should see the default Vite + React screen.
Congratulations — your frontend environment is running with:
React
TypeScript
Vite
pnpm
You now have a modern baseline that rivals any production frontend setup.
7. Add a Simple Component to Test Type Safety
Let’s create a quick example to confirm our setup is working perfectly.
// src/components/Greeting.tsx
interface GreetingProps {
name: string;
age?: number;
}
export function Greeting({ name, age }: GreetingProps) {
return (
<div>
<h1>Hello, {name}!</h1>
{age && <p>Age: {age}</p>}
</div>
);
}Then import it into App.tsx:
import { Greeting } from './components/Greeting';
function App() {
return (
<main>
<Greeting name="Ava" age={27} />
</main>
);
}
export default App;If you accidentally pass the wrong prop type (e.g., age="27"), TypeScript will flag it immediately — proof your setup is working.
8. Version and Commit
Before we move forward, commit your setup:
git add .
git commit -m "Setup React + TypeScript app with Vite and pnpm"You now have a reproducible, type-safe, and scalable React foundation ready for future integration with the backend.
9. Next Steps
In Part 1.2, we’ll start exploring React components and props in depth — building reusable, composable UI units that make large apps maintainable.
You’ve laid the groundwork for a professional frontend architecture.
From here, you’ll move from setup to structure.
Related
Part 2.5 — Consuming REST APIs with Shared Types & Error Handling Strategies
Real-world frontends need safety: typed API responses, predictable errors, and consistent client logic. This post teaches you the patterns professionals use to integrate React with REST cleanly.
Part 2.4 — UX State: Loading, Error, and Empty States in React
A React app becomes truly usable when it handles all states gracefully: loading, error, empty, and success. In this post, you’ll learn the UX patterns professionals rely on.
Part 2.3 — Handling Forms, Validation & Submission to REST Endpoints
Forms connect users to data. In this post, you’ll learn how to validate input, manage UX states, and submit cleanly typed data to real REST endpoints.
Comments