React Day 1: Auth MFE (Vite + TS + Router + MSW) — Code + Tests, Line-by-Line
React Day 1: Auth MFE (Vite + TS + Router + MSW) — Code + Tests, Line-by-Line
Beginner-friendly, Windows/PowerShell ready. Every step has full code and a matching test you can run immediately.
Introduction
Why this stack? Vite gives instant reloads, TypeScript catches mistakes early, React Router handles page navigation, and MSW pretends to be an API so you can practice without a backend. Tests run with Vitest and React Testing Library to check behavior like a user would.
How to learn here: For each example, copy the code files, run the app, then copy the test file and run the tests. Small wins → confidence builds.
React Fundamentals — 10-minute Primer
- Component: A function that returns UI (JSX). You build apps by composing components.
- JSX: HTML-like syntax inside TS/JS that compiles to React calls. It’s not a string.
- State: A component’s memory. Changing it (e.g.,
setCount
) re-renders the UI. - Props: Inputs passed from parent → child (like function parameters).
- Events: Handlers like
onClick
/onChange
trigger state updates. - Effects:
useEffect
runs side-effects (fetching, subscriptions) when data changes. - Declarative vs Imperative: Declarative says what the UI should be; React figures out how to update the DOM.
- Routing: Switches which component shows for a URL (no full page reload).
- Mocking: MSW intercepts
fetch
calls and replies with fake JSON for predictable learning. - Testing mindset: Tests describe behavior (what a user sees/does), not implementation details.
Prerequisites & Setup (PowerShell)
Prerequisites
- Node.js 18+ (prefer 20)
- Git (optional but recommended)
- VS Code or IntelliJ IDEA (Community)
- Basic terminal/PowerShell comfort
Quick Verify
node -v
npm -v
git --version
Workspace & First Project
# 1) Create workspace
PS C:\mycode\react> mkdir day1; cd day1
# 2) Scaffold Vite + React + TS for ex1
PS C:\mycode\react\day1> npm create vite@latest ex1-basic-template -- --template react-ts
# 3) Install & run
PS C:\mycode\react\day1\ex1-basic-template> npm install
PS C:\mycode\react\day1\ex1-basic-template> npm run dev
# 4) Open http://localhost:5173
One-time Testing Setup (Vitest + RTL + MSW)
Do this once in ex1-basic-template
, then copy the folder for each next example.
PS C:\mycode\react\day1\ex1-basic-template> npm i -D vitest @testing-library/react @testing-library/user-event jsdom @testing-library/jest-dom msw
# Optional (if you want Playwright later)
# PS ...> npm i -D @playwright/test; npx playwright install
vitest.config.ts (root)
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"]
}
});
src/test/setup.ts
import "@testing-library/jest-dom";
Update package.json scripts:
{
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview",
"test": "vitest --run"
}
}
npm run test
Folder Structure (for all examples in this session)
We’ll create a new folder per example by copying the previous one.
day1/
ex1-basic-template/
ex2-hello-component/
ex3-hello-props/
ex4-counter-state/
ex5-compose-components/
ex6-router-setup/
ex7-router-links/
ex8-msw-setup/
ex9-users-fetch/
ex10-users-route/
Architecture (beginner-friendly diagram)
Think of the app in two parts: the browser app (your React components + Router) and a mock layer (MSW) that fakes API responses so you can practice safely.
flowchart LR subgraph Browser["React App (Vite + TS)"] A[Components
JSX + State + Props] R[React Router] U[Users Page] C[Counter] end subgraph MockLayer["MSW (Mock Service Worker)"] M[Handlers
/api/users, /api/auth/login] end A --> R R --> U A --> C U -- fetch --> M M -- JSON --> U
Before you start the examples
- Copy the previous example folder to create the next one.
- Open the new folder. If needed, run
npm install
. - Replace file contents with the full code blocks below.
- Run
npm run dev
and test withnpm run test
.
# Pattern (Windows PowerShell)
PS C:\mycode\react\day1> robocopy <prev> <next> /e
PS C:\mycode\react\day1\<next>> npm install
PS C:\mycode\react\day1\<next>> npm run dev
PS C:\mycode\react\day1\<next>> npm run test
npm run dev
.10 Examples (ex1 → ex10) — Full Code + Tests
ex1 — Basic Vite + TS Template
Goal: Minimal app renders a heading.
Files (Full code — copy/paste)
src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);
src/App.tsx
export default function App() {
return (
<div style={{ padding: 24 }}>
<h1>Hello Vite + React + TS</h1>
<p className="muted">Baseline template</p>
</div>
);
}
Test (Vitest + RTL)
src/App.test.tsx
import { render, screen } from "@testing-library/react";
import App from "./App";
test("renders heading", () => {
render(<App />);
expect(screen.getByRole("heading", { name: /Hello Vite/i })).toBeInTheDocument();
});
L1 BDD idea
Feature: App boot
Scenario: Show hello message
Given I open the app
Then I should see "Hello Vite + React + TS"
ex2 — Hello Component
Goal: Create and render a reusable component.
Files (Full code — copy/paste)
src/Hello.tsx
export default function Hello() {
return <h2>Hello React</h2>;
}
src/App.tsx
import Hello from "./Hello";
export default function App() {
return (
<div style={{ padding: 24 }}>
<h1>ex2 — Hello Component</h1>
<Hello />
</div>
);
}
Test
src/Hello.test.tsx
import { render, screen } from "@testing-library/react";
import Hello from "./Hello";
test("shows Hello React", () => {
render(<Hello />);
expect(screen.getByText("Hello React")).toBeInTheDocument();
});
L1 BDD idea
Feature: Hello component
Scenario: Show greeting
Given I load the app
Then I should see "Hello React"
ex3 — Props
Goal: Pass data from parent to child.
Files (Full code — copy/paste)
src/Hello.tsx
type HelloProps = { name: string };
export default function Hello({ name }: HelloProps) {
return <h2>Hello {name}</h2>;
}
src/App.tsx
import Hello from "./Hello";
export default function App() {
return (
<div style={{ padding: 24 }}>
<h1>ex3 — Props</h1>
<Hello name="Ganesh" />
</div>
);
}
Test
src/Hello.props.test.tsx
import { render, screen } from "@testing-library/react";
import Hello from "./Hello";
test("greets by name", () => {
render(<Hello name="Ganesh" />);
expect(screen.getByText("Hello Ganesh")).toBeInTheDocument();
});
L1 BDD idea
Feature: Props
Scenario Outline: Greet by name
Given I pass the name "<name>"
Then I should see "Hello <name>"
Examples:
| name |
| Ganesh |
| Ada |
ex4 — State (useState)
Goal: Manage changing data with a counter.
Files (Full code — copy/paste)
src/Counter.tsx
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>+1</button>
</div>
);
}
src/App.tsx
import Counter from "./Counter";
export default function App() {
return (
<div style={{ padding: 24 }}>
<h1>ex4 — State</h1>
<Counter />
</div>
);
}
Test
src/Counter.test.tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Counter from "./Counter";
test("increments on click", async () => {
const user = userEvent.setup();
render(<Counter />);
await user.click(screen.getByRole("button", { name: "+1" }));
expect(screen.getByText("Count: 1")).toBeInTheDocument();
});
L1 BDD idea
Feature: Counter
Scenario: Increment
Given the counter starts at 0
When I click "+1"
Then I see "Count: 1"
ex5 — Compose Components
Goal: Use multiple components together.
Files (Full code — copy/paste)
src/Hello.tsx
export default function Hello({ name = "React" }: { name?: string }) {
return <h2>Hello {name}</h2>;
}
src/App.tsx
import Hello from "./Hello";
import Counter from "./Counter";
export default function App() {
return (
<div style={{ padding: 24 }}>
<h1>ex5 — Composition</h1>
<Hello name="Ganesh" />
<Counter />
</div>
);
}
Test
src/App.compose.test.tsx
import { render, screen } from "@testing-library/react";
import App from "./App";
test("shows Hello and Counter", () => {
render(<App />);
expect(screen.getByText("Hello Ganesh")).toBeInTheDocument();
expect(screen.getByText(/Count:/)).toBeInTheDocument();
});
L1 BDD idea
Feature: Composition
Scenario: Independent pieces
Given a greeting and a counter
Then both render on the page
ex6 — Router Setup
Goal: Switch views by URL using React Router.
PS ...\ex6-router-setup> npm install react-router-dom
Files (Full code — copy/paste)
src/App.tsx
import { BrowserRouter, Routes, Route } from "react-router-dom";
function Home() { return <h2>Home</h2>; }
function About() { return <h2>About</h2>; }
export default function App() {
return (
<BrowserRouter>
<div style={{ padding: 24 }}>
<h1>ex6 — Router</h1>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
</BrowserRouter>
);
}
Test
src/App.router.test.tsx
import { render, screen } from "@testing-library/react";
import { MemoryRouter, Routes, Route } from "react-router-dom";
function About() { return <h2>About</h2>; }
test("navigates to /about", () => {
render(
<MemoryRouter initialEntries={["/about"]}>
<Routes>
<Route path="/about" element={<About />} />
</Routes>
</MemoryRouter>
);
expect(screen.getByRole("heading", { name: "About" })).toBeInTheDocument();
});
L1 BDD idea
Feature: Routing
Scenario: About page by URL
Given I navigate to "/about"
Then I see "About"
ex7 — Navigation Links
Goal: Navigate declaratively with <Link>.
Files (Full code — copy/paste)
src/App.tsx
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
function Home() { return <h2>Home</h2>; }
function About() { return <h2>About</h2>; }
export default function App() {
return (
<BrowserRouter>
<div style={{ padding: 24 }}>
<h1>ex7 — Links</h1>
<nav style={{ display:"flex", gap:12 }}>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
</BrowserRouter>
);
}
Test
src/App.links.test.tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { MemoryRouter, Routes, Route, Link } from "react-router-dom";
function Home() { return <h2>Home</h2>; }
function About() { return <h2>About</h2>; }
function App() {
return (
<div>
<nav>
<Link to="/">Home</Link> | <Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
);
}
test("clicking link navigates", async () => {
const user = userEvent.setup();
render(
<MemoryRouter>
<App />
</MemoryRouter>
);
await user.click(screen.getByRole("link", { name: "About" }));
expect(screen.getByRole("heading", { name: "About" })).toBeInTheDocument();
});
L1 BDD idea
Feature: Links
Scenario: Navigate via link
Given I click "About"
Then the About page is shown
ex8 — MSW Setup (Mock API)
Goal: Intercept fetch
and return fake data.
PS ...\ex8-msw-setup> npm i -D msw
Files (Full code — copy/paste)
public/mockServiceWorker.js (generated by MSW init)
# Run:
# npx msw init public/ --save --no-open
# This creates public/mockServiceWorker.js for you.
src/mocks/handlers.ts
import { http, HttpResponse } from "msw";
export const handlers = [
http.get("/api/users", () => {
return HttpResponse.json([
{ id: 1, name: "Ada" },
{ id: 2, name: "Linus" }
]);
}),
];
src/mocks/browser.ts
import { setupWorker } from "msw/browser";
import { handlers } from "./handlers";
export const worker = setupWorker(...handlers);
src/main.tsx (start worker in dev)
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
async function enableMocking() {
if (import.meta.env.DEV) {
const { worker } = await import("./mocks/browser");
await worker.start({ serviceWorker: { url: "/mockServiceWorker.js" }});
}
}
await enableMocking();
createRoot(document.getElementById("root")!).render(
<StrictMode><App /></StrictMode>
);
Test
src/mocks/server.test.ts (node server for tests)
import { setupServer } from "msw/node";
import { handlers } from "./handlers";
const server = setupServer(...handlers);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test("msw handlers are registered", () => {
// If setup runs, test passes. (Sanity check for Day1)
expect(typeof server.listen).toBe("function");
});
L1 BDD idea
Feature: Mock API
Scenario: Users endpoint
When I call GET /api/users
Then I receive a list of users (Ada, Linus)
ex9 — Fetch Users Component
Goal: Fetch from /api/users
(MSW) and render names.
Files (Full code — copy/paste)
src/Users.tsx
import { useEffect, useState } from "react";
type User = { id: number; name: string };
export default function Users() {
const [users, setUsers] = useState<User[] | null>(null);
useEffect(() => {
fetch("/api/users").then(r => r.json()).then(setUsers);
}, []);
if (!users) return <p>Loading...</p>;
return (
<ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
);
}
src/App.tsx
import Users from "./Users";
export default function App() {
return (
<div style={{ padding: 24 }}>
<h1>ex9 — Users</h1>
<Users />
</div>
);
}
Test
src/Users.test.tsx
import { render, screen, waitFor } from "@testing-library/react";
import Users from "./Users";
import { setupServer } from "msw/node";
import { handlers } from "./mocks/handlers";
const server = setupServer(...handlers);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test("renders names from mocked API", async () => {
render(<Users />);
await waitFor(() => {
expect(screen.getByText("Ada")).toBeInTheDocument();
expect(screen.getByText("Linus")).toBeInTheDocument();
});
});
L1 BDD idea
Feature: Users list
Scenario: Show data
Given the API returns (Ada, Linus)
When the Users page loads
Then I see Ada and Linus on the screen
ex10 — Integrate Users with Router
Goal: Add a /users
route so nav → page → fetch works end-to-end (with MSW).
Files (Full code — copy/paste)
src/App.tsx
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
import Users from "./Users";
function Home() { return <h2>Home</h2>; }
function About() { return <h2>About</h2>; }
export default function App() {
return (
<BrowserRouter>
<div style={{ padding: 24 }}>
<h1>ex10 — Users Route</h1>
<nav style={{ display:"flex", gap:12 }}>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/users">Users</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users" element={<Users />} />
</Routes>
</div>
</BrowserRouter>
);
}
Test
src/App.users-route.test.tsx
import { render, screen } from "@testing-library/react";
import { MemoryRouter, Routes, Route, Link } from "react-router-dom";
import { setupServer } from "msw/node";
import { handlers } from "./mocks/handlers";
import Users from "./Users";
const server = setupServer(...handlers);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
function Home() { return <h2>Home</h2>; }
function About() { return <h2>About</h2>; }
function App() {
return (
<div>
<nav>
<Link to="/">Home</Link> | <Link to="/about">About</Link> | <Link to="/users">Users</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users" element={<Users />} />
</Routes>
</div>
);
}
test("navigates to /users and shows list", async () => {
render(
<MemoryRouter initialEntries={["/users"]}>
<App />
</MemoryRouter>
);
expect(await screen.findByText("Ada")).toBeInTheDocument();
expect(await screen.findByText("Linus")).toBeInTheDocument();
});
L1 BDD idea
Feature: Users route
Scenario: Navigate and load data
Given I visit "/users"
Then I see the mocked users list
Comments
Post a Comment