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

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.

Tip: Use the Copy buttons on code blocks. Each block contains the full code you need for that file at that step.

Introduction

Welcome! Today you’ll build a tiny, realistic React app (an “Auth-flavored” micro-frontend) and learn by doing. You’ll write small components, add routing, and fetch data — with tests at every step so you know it works.

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"
  }
}
Run tests: 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

  1. Copy the previous example folder to create the next one.
  2. Open the new folder. If needed, run npm install.
  3. Replace file contents with the full code blocks below.
  4. Run npm run dev and test with npm 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
Note: If Vite gets confused after copying, stop and restart 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.

Install once in this folder:
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.

Install once in this folder (if not already):
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

Popular posts from this blog

Day 1: React Fundamentals — Build an Auth MFE

React Day 1: Auth MFE (Vite + TS + Router + MSW) — End-to-End, Line-by-Line

Building Scalable AWS VPC Infrastructure with Terraform Modules for SaaS Applications