Step 3 — Vite, TypeScript Config & Project Structure
iKanban's frontend (vibe-frontend/) is built with Vite instead of Create React App or webpack. Vite serves your source files directly over native ES modules during development (near-instant startup, instant hot reload) and bundles with Rollup for production. Let's look at the two config files that shape every import you'll write.
vite.config.ts — the @/ alias
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
shared: path.resolve(__dirname, "../shared"),
},
},
This is why every component imports like this instead of ../../../components/ui/badge:
import { Badge } from '@/components/ui/badge';
import type { Tag } from 'shared/types';
@ always means vibe-frontend/src/, no matter how deeply nested the importing file is. shared points outside vibe-frontend/ entirely, to a sibling shared/ directory at the repo root — that's where the hand-maintained shared/types.ts file lives, which you'll meet in Step 9 when we trace the Tags feature into the backend.
Vite also proxies /api to the local backend during development:
server: {
proxy: {
"/api": { target: `http://localhost:${process.env.BACKEND_PORT || "3001"}` },
},
},
So fetch('/api/tags') from the browser during pnpm dev transparently reaches the Rust backend on port 3001 — no CORS configuration needed locally.
tsconfig.json — strict mode
iKanban's tsconfig.json enables strict: true. In practice this means:
- Every variable's type is known or explicitly
any(rare, and avoided — CLAUDE.md core rule #14 bansas any/@ts-ignoreoutright). - A value that might be
null/undefinedmust be checked before you use it — this is why you'll seetag?.team_idandexisting?.color.as_deref()-style guards throughout the codebase (Rust'sOption<T>and TypeScript'sstrictnull checks solve the same problem on both sides of the stack).
Where files live
A quick map of vibe-frontend/src/:
| Directory | What's in it |
|---|---|
pages/ | Route-level components (one per URL, e.g. TeamMembers.tsx) |
components/ | Reusable pieces, organized by feature (components/dialogs/tasks/, components/docs/) |
components/ui/ | shadcn/ui primitives (Button, Dialog, Badge) — Step 4 |
hooks/ | Custom hooks wrapping TanStack Query (useTags.ts) — Step 6 and Step 11 |
stores/ | Zustand stores for client-only UI state — Step 5 |
lib/ | Framework-agnostic utilities: api.ts (the fetch client), docsRegistry.ts (this very tutorial's registry) |
contexts/ | React Context providers (WorkspaceContext) |
This mirrors the backend's crates/remote/src/{db,routes}/ split you'll trace in Part 2 — a repository/data layer, a routing layer, and a presentation layer, just on the other side of the network boundary.
Checkpoint
Run the dev server and confirm hot reload:
cd vibe-frontend && pnpm dev
Edit any string in a .tsx file under src/pages/ and watch the browser update without a full reload.
Continue to Step 4 — TailwindCSS v4 + shadcn/ui Components, where we style the component from Step 2.