# Step 3 — Vite, TypeScript Config & Project Structure

iKanban's frontend (`vibe-frontend/`) is built with [Vite](https://vitejs.dev) 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](https://rollupjs.org) for production. Let's look at the two config files that shape every import you'll write.

## `vite.config.ts` — the `@/` alias

```ts
resolve: {
  alias: {
    "@": path.resolve(__dirname, "./src"),
    shared: path.resolve(__dirname, "../shared"),
  },
},
```

This is why every component imports like this instead of `../../../components/ui/badge`:

```ts
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:

```ts
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 bans `as any`/`@ts-ignore` outright).
- A value that might be `null`/`undefined` must be checked before you use it — this is why you'll see `tag?.team_id` and `existing?.color.as_deref()`-style guards throughout the codebase (Rust's `Option<T>` and TypeScript's `strict` null 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:

```bash
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.
