Skip to main content

Posts

🧱 Part 17 — Advanced TypeScript Patterns (Branded Types, Results, Exhaustive Checks) 🧠

When your app grows, types become your safety net . Here are advanced—but approachable—TypeScript patterns that prevent entire classes of bugs. Every snippet is commented and copy‑paste ready. 1) Branded (Opaque) Types — prevent mixing IDs/units Avoid passing a ProjectId where a TaskId is expected. // src/types/brand.ts // Create an opaque subtype of T (compile-time only) export type Brand<T, B extends string> = T & { readonly __brand: B }; export type TaskId = Brand<string, 'TaskId'>; export type ProjectId = Brand<string, 'ProjectId'>; export const TaskId = (s: string): TaskId => s as TaskId; export const ProjectId = (s: string): ProjectId => s as ProjectId; // Usage function loadTask(id: TaskId) {/* impl */} // loadTask('123'); // ❌ plain string not allowed loadTask(TaskId('123')); // ✅ branded Brands exist only at compile time; runtime values are plain strings. 2) Discriminated Unions + Exhaustiv...

🧱 Part 16 — Accessibility First (Semantics, Keyboard, ARIA, Focus, Color)

Accessibility (a11y) is not optional : it helps keyboard users, screen‑reader users, and frankly everyone (think: low contrast screens, bright sun, fatigue). This part is beginner‑friendly and packed with commented patterns you can paste in today. 🎯 Goals Semantic HTML & landmarks (nav, main, header, footer) Keyboard support (Tab order, focus outlines, ESC to close) Visible focus with :focus-visible Skip link , visually hidden text, live regions Readable colors (contrast), reduced motion ARIA for dialog , tabs , and form errors 1) Baseline: semantic layout + skip link Make the structure obvious to assistive tech and provide a quick jump to content. // src/app/Layout.tsx import { SkipLink } from '@/components/a11y/SkipLink'; export default function Layout({ children }: { children: React.ReactNode }) { return ( <div> <SkipLink target="#main" /> <header role="banner" className="p-4 border-b...

🧱 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 *...