CHAPTER 02 · START

Setup & Your First Project

The goal isn't "software installed". It's a reproducible environment: your laptop, a teammate's laptop, CI, and an agent's sandbox all produce the same result from the same project.

⏱ ~45 min (hands-on)UseThe env-var exposure rule is Deep
By the end of this chapter you should be able to:
  • Install and pin a Node version with a version manager, and manage dependencies with pnpm.
  • Read the six most important options in a tsconfig.json.
  • Explain why env vars prefixed VITE_ / NEXT_PUBLIC_ must never hold secrets.
  • Create your first React + TS project and write an AGENTS.md for it.

Opening: "works on my machine"

Opening question

Same repo. pnpm dev works for your teammate; you get SyntaxError: Unexpected token; the agent's sandbox reports Cannot find module. The code is identical — why are the results different?

Where the answer lies: "the code" is only one input to the result. The other three are the Node version, the package manager and its version, and the exact installed dependency versions (decided by the lockfile). If any of them differs, the result may differ. A reproducible environment writes all three into the repo.

Node version .nvmrc / mise.toml "engines" field Package manager "packageManager" pnpm@10.x Exact dep versions pnpm-lock.yaml must be committed Your code + config src/ · tsconfig.json .env.example (no real values) Same result (you / teammate / CI / agent)
Put all four inputs in the repo and the environment becomes reproducible. The only thing that stays out is real secrets, injected separately via environment variables.

Step 1: install Node through a version manager Use

Don't download the installer from the website. Sooner or later you'll maintain projects that need different Node versions, and a version manager lets each project use its own automatically. We recommend mise (one tool for Node, pnpm, Python, Go and more) or fnm (Node only, minimal).

Node's versioning rule: even-numbered releases become long-term support (LTS); odd ones are short-lived "Current" releases. Use LTS in production. As of September 2026, Node 24 is LTS and Node 26 becomes LTS in October.

Step 2: install pnpm Use

Put "packageManager": "pnpm@10.x.x" (an exact version) in package.json; many tools — including CI and deployment platforms — use it to pick the same package manager version.

The most common commands, mapped to tools you know:

TaskpnpmAnalogy
Install everything per the lockfilepnpm installgo mod download
Strict install in CIpnpm install --frozen-lockfilenpm ci
Add a runtime dependencypnpm add zodgo get
Add a dev dependencypnpm add -D vitestMaven test scope
Run a scriptpnpm dev / pnpm run buildmake dev
Run a dependency's CLIpnpm exec tsc (or pnpm tsc)—
Download and run a tool oncepnpm dlx create-vitenpx
Why is this package installed?pnpm why lodashgo mod why

Step 3: the editor — your most important learning tool Use

VS Code, Cursor, Zed or JetBrains WebStorm all work. They embed the TypeScript language service: while you type, it type-checks continuously in the background.

The single most useful habit during the transition: hover for types

Hover over any variable, function or component and the editor shows its full type. When reading agent-generated code, this is the fastest way to understand the shape of the data — much faster than reading docs. Cmd/Ctrl-click jumps to the definition, including library type declarations inside node_modules.

Suggested extensions: Biome (format + lint; or ESLint + Prettier), Tailwind CSS IntelliSense (class name hints), Error Lens (shows errors inline at the end of the line).

Step 4: install a coding agent

This guide isn't tied to any one tool. Popular terminal agents include Claude Code (Anthropic), Codex CLI (OpenAI) and pi (open source, minimal, extensible in TypeScript). They all work on the same principles (Chapter 12 takes them apart). Follow each tool's official install instructions; most support npm install -g.

Once installed, do one thing first: in an empty directory, ask it to "describe the environment you're running in" and check it can run node -v and pnpm -v correctly. That confirms the agent's execution environment matches your terminal.

Step 5: your first TypeScript program (no framework)

Leave React aside for now. Use the bare minimum to feel that "type checking" and "running" are two separate things:

Running these two commands yourself teaches more than reading the definition of "type erasure" ten times. You'll see: the runner is lenient, the type checker is strict — and what you want guarding the door is the latter.

Step 6: scaffold LinkNote's frontend

While learning, start with the plain Vite + React template: fewest concepts, one runtime (the browser). You'll use it for React in Chapter 06 and switch to a full-stack framework in Chapter 07.

Other common scaffolding commands (check each official site):

Reading the project structure

PathWhat it isCommit it?
src/Your source. main.tsx is the entry, App.tsx the root componentYes
public/Static files copied as-is into the output (icons, etc.)Yes
index.htmlThe first file the browser loads; it references src/main.tsxYes
package.json / pnpm-lock.yamlDeclared deps / exact versionsYes
tsconfig*.jsonTypeScript configurationYes
vite.config.tsBuild tool configurationYes
node_modules/Installed deps (rebuildable any time)No
dist/Build outputNo
.env / .env.localLocal env vars (may contain secrets)No (commit .env.example)

tsconfig.json: these options are all you need to read Use

Environment variables: which ones reach the browser Deep

This is one of the easiest mistakes to make and one of the most costly, which is why even a "setup" chapter goes down to the mechanism.

In backend work, env vars exist only inside the server process — naturally private. In a frontend build, however, the bundler replaces certain env var references with literal values at build time, writing them into the JS files sent to browsers. To prevent accidental leaks, frameworks only do this for variables with a specific prefix.

The rule

A variable with a public prefix such as VITE_ / NEXT_PUBLIC_ is effectively published. API keys and database connection strings must never use those prefixes. If the browser needs to call an LLM, the correct approach is for the browser to call your own server, which calls the LLM with the key (Chapter 11).

Step 7: a handbook for your agent

Nearly every coding agent reads a project instructions file from the repo root at startup: AGENTS.md (a cross-tool convention) or CLAUDE.md (Claude Code). Think of it as onboarding docs for a new colleague. Write it as soon as the project exists, and be specific:

Chapter 13 covers how to write this file well. For now, remember one principle: the second time you find yourself telling the agent the same thing, write it into AGENTS.md.

Common errors at a glance

ErrorUsual causeFix
command not found: pnpmNot installed, or the shell didn't load the version managerReopen the terminal; check mise/fnm shell integration
Cannot find module 'x'Deps not installed, or undeclared under pnpm's strict layoutpnpm install; or pnpm add x
ERR_REQUIRE_ESM / Cannot use import statement outside a moduleMixing ES modules and CommonJS (Chapter 03)Check "type": "module" and file extensions
EADDRINUSE: address already in use :::5173Port taken (often a dev server still running)Kill the old process or change the port
Unsupported engineNode version doesn't satisfy the projectSwitch per the project's mise.toml / .nvmrc
Tell the agent
Vague
Precise
"The project won't start, fix it."
"pnpm dev fails (full output below). Don't change any code yet: first tell me the Node version, the pnpm version, and whether the lockfile matches package.json; decide whether this is an environment or a code problem; then propose a fix."

Diagnose first, change second — the most effective way to stop an agent from "editing things until the error goes away".

Self-check

1. You put VITE_OPENAI_KEY=sk-... in .env. It's only used on a "settings" page that requires login. Is that safe?

No. VITE_-prefixed variables are inlined into the JS at build time. JS files are static assets that can be downloaded without logging in (login protects page data, not the bundled scripts). Anyone can open the file and search for the key. Drop the prefix and use it only on the server.

2. Why must the lockfile be committed while node_modules must not?

The lockfile records the exact version and checksum of every dependency, including transitive ones — the key to reproducibility — and it's small text. node_modules can be rebuilt from the lockfile at any time, is huge, and may contain OS-specific binaries.

3. To fix a type error, an agent set strict to false in tsconfig and then reported "type check passes". What do you make of it?

It silenced the check instead of fixing the problem. Turning off strict stops reporting many real defects (like null access). Revert that change, require the agent to fix the specific error under strict mode, and add "never change tsconfig strictness options" to AGENTS.md.