Skip to main content

Posts

🧱 Part 15 — Code Splitting & Suspense (Lazy Routes, Error Boundaries, Preloading, Skeletons) 🧩

Big bundles make apps feel sluggish. Code splitting ships only what’s needed when it’s needed. Suspense gives you a clean way to show fallbacks (spinners/skeletons) while chunks/data load. This guide is beginner‑friendly and copy‑paste safe. 🎯 What you’ll do Split code at route and component level with React.lazy Wrap chunks in Suspense with polished fallbacks Add Error Boundaries so chunk or render failures don’t blank your app Preload chunks on hover/touch for instant nav Use Vite goodies like import.meta.glob for large lazy trees Show skeleton UIs instead of jumpy spinners Works with Vite + React Router 6+ (what we’ve been using). 1) Route‑level code splitting with React.lazy Each route becomes its own chunk. React loads it on demand. // src/app/AppRouter.tsx import { BrowserRouter, Routes, Route } from 'react-router-dom'; import { Suspense, lazy } from 'react'; import Layout from '@/app/Layout'; import NotFound from '@/pages...

🧱 Part 14 — Performance Tuning (memo, useCallback, Profiler, Virtualization) ⚡️

Make your app feel snappy by avoiding unnecessary work. This part is practical, beginner‑friendly, and packed with commented examples you can paste in. 🎛️ The Big Ideas Render less: memoize components and values so React doesn’t re-render needlessly. Do less: avoid heavy computations during render; cache them with useMemo . Work smarter: virtualize long lists so the DOM only contains visible rows. Measure first: use the React Profiler (and browser Performance tab) to verify improvements. Golden rule: Measure → Change → Measure again. 1) React.memo — skip re-renders when props didn’t change // src/components/perf/ExpensiveRow.tsx import React from 'react'; type RowProps = { id: string; title: string; done: boolean; onToggle: (id: string) => void; }; function RowBase({ id, title, done, onToggle }: RowProps) { // pretend this is heavy (expensive formatting, etc.) for (let i = 0; i < 200_000; i++); // demo CPU work return ( <div ...

🧱 Part 13 — Testing Strategy (Vitest + Testing Library + MSW) 🧪

A great test setup is fast, reliable, and mirrors how users actually use your app. We’ll set up Vitest (fast runner), React Testing Library (test behavior, not internals), and MSW (mock server) — with clear, commented examples for components , hooks , and Redux slices , plus API integration tests. Philosophy: Test the public surface (what users see/do), not implementation details. Prefer queries by role/label/text over test IDs. Keep unit tests small; add a few integration tests where flows matter. 1) Install test tooling pnpm add -D vitest @testing-library/react @testing-library/user-event @testing-library/jest-dom jsdom pnpm add -D msw whatwg-fetch # MSW for API mocks, fetch polyfill for jsdom 2) Configure Vitest (Vite + jsdom + setup file) // vitest.config.ts import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], test: { environment: 'jsdom...

🧱 Part 12 — Storybook for React + TypeScript (Document & Test Your UI) 📚

Now that we have design system primitives (Button, Input, Card), we need a way to document, preview, and test them in isolation. That’s where Storybook comes in. Storybook provides: A component playground outside your app. Interactive docs for your design system. Addons for accessibility (a11y) , viewport testing , and interaction testing . 1) Install Storybook Run the official setup tool: npx storybook@latest init Detects React + TypeScript + Vite automatically. Creates a .storybook/ folder with config. Adds example stories in src/stories/ . Start Storybook: pnpm storybook This launches on http://localhost:6006 . 2) Configure TypeScript support In .storybook/tsconfig.json , extend your root config: { "extends": "../tsconfig.json", "include": ["../src", "./*.ts", "./*.tsx"] } 3) Write your first story (Button) Stories live alongside components or in src/stories/ . Each story is a different s...

🧱 Part 11 — Design System & Theming (Tokens • CSS Variables • Tailwind • A11y) 🎨

A design system makes your UI consistent, accessible, and fast to iterate. We’ll define design tokens (colors, spacing, radii, typography) as CSS variables, wire light/dark themes , and build typed UI primitives (Button, Input, Card). Everything is copy‑paste ready and verified for errors. 🧩 Concepts in 30 seconds Tokens : the single source of truth for colors/spacing/typography. Primitives : small, reusable components that read tokens. Variants : controlled style options (e.g., primary | secondary | danger ). Theming : switch token values for light/dark without changing component code. 1) Tokens as CSS variables (light & dark) We use HSL/hex via var(--token) so classes can read them with Tailwind’s arbitrary values. /* src/styles/tokens.css */ :root { /* Base colors */ --color-bg: #ffffff; --color-fg: #111111; --color-muted: #6b7280; /* slate-500 */ --color-primary: #2563eb; /* blue-600 */ --color-danger: #dc2626; /* red-600 *...

🧱 Part 10 — Context API & Dependency Injection (Lightweight Global State) 🪢

Sometimes you don’t need Redux—just a small, type‑safe way to share a value or service across components without prop‑drilling. That’s exactly what React Context is for. Below is a clean, error‑free , copy‑paste‑ready setup with thorough explanations and commented code . 🧩 Core Ideas Context : A container that can provide a value deep in the tree. Provider : A component that owns the value and makes it available to its descendants. useContext : A hook to read that value. Dependency Injection (DI) : Pass a service (e.g., API client) via Context so components can swap implementations (e.g., mock vs real). 1) Theme Context (with safe typing + memo + persistence) A minimal, production‑ready pattern: Throws a helpful error if used outside its provider. Memoizes the context value to avoid unnecessary re‑renders. Persists the theme to localStorage and respects system preference on first load. // src/contexts/ThemeContext.tsx import { createContext, useContext, useEffec...

🧱 Part 9 — Global State with Redux Toolkit (RTK + TypeScript) 🗂️

Some state doesn't come from the server and shouldn't be fetched via HTTP—think theme (light/dark) , active filters , sidebar open/closed , currently selected task ID , etc. This is global/app state . For this, Redux Toolkit (RTK) gives you a clean, predictable, and type‑safe pattern. Quick mental model: React state → great for local component-only state. Redux (RTK) → great for app-wide UI state that multiple components need. TanStack Query → great for server data (caching, refetching, syncing). 🧩 Core Concepts (explained simply) Store : a single object tree that holds your app’s global state. Slice : a focused piece of state (e.g., ui , filters ) with its reducers and actions created by RTK. Reducer : a pure function (state, action) → newState . In RTK you write mutating-looking code; under the hood Immer produces an immutable update for you. Action : a plain object { type, payload } describing what happened . Selector : a function that reads/der...