Web Dev Tools Guide

Master the toolchain behind modern web development โ€” Node.js and npm, package.json, bundlers and Vite, TypeScript, linters, browser DevTools, environment variables, testing, and shipping to production. Every command has a Copy button.

Copy button on each code block  ยท  โ— Beginner โ— Intermediate โ— Advanced

01 The Modern Web Toolchain Beginner

A single index.html file is enough to start, but real projects quickly grow a toolchain โ€” a set of programs that work together to run, check, bundle and ship your code. The good news: every tool has one job, and once you know the job, the maze becomes a map.

This guide walks the whole chain from the runtime you install to the command that puts your site online. Nothing here requires a framework โ€” these are the fundamentals behind React, Vue, Next.js and everything else.

What each tool actually does

Node.js
JavaScript runtime โ€” runs JS outside the browser
npm
Package manager โ€” installs libraries and runs scripts
Vite
Dev server + bundler with instant hot reload
TypeScript
Typed JavaScript, compiled to plain JS
ESLint
Finds bugs and bad patterns in your code
Prettier
Formats code so you never argue about style
DevTools
Inspect, debug and profile in the browser
Vitest
Runs automated tests for your code

A typical day-to-day workflow

  1. Install dependencies once with npm install.
  2. Develop with a live dev server: npm run dev.
  3. Check as you go: npm run lint and npm run test.
  4. Build an optimized bundle: npm run build.
  5. Preview the production build locally: npm run preview.
  6. Deploy the output folder to a host.
Mental model: the browser only understands HTML, CSS and JavaScript. Every other tool exists to turn nicer source code into those three things โ€” or to catch mistakes before the browser ever sees them.
๐Ÿ’ก Tip: Do not install tools globally as a habit. Project-local versions (via package.json) keep every machine and teammate in sync.

02 Node.js & npm Beginner

Node.js is a JavaScript runtime built on Chrome's V8 engine. It lets you run JavaScript from the terminal โ€” for scripts, servers, and the build tools that power web development. npm (Node Package Manager) ships with Node and is how you install libraries and run project commands.

Check what you have

node --version
npm --version

# Expected output (versions will differ)
# v22.11.0
# 10.9.0

Install Node the right way

Prefer a version manager so you can switch Node versions per project. nvm (macOS/Linux) and fnm are the common choices.

# macOS / Linux โ€” install nvm from nvm.sh, then:
nvm install --lts
nvm use --lts
nvm alias default lts/*

# pin a version for a project
echo "22" > .nvmrc

# Windows: use nvm-windows or fnm

Run JavaScript without a browser

# run a file
node app.js

# open the interactive REPL
node

# run a one-off expression
node -e "console.log('Hello from Node')"

Start a project

npm init -y creates a default package.json. Without -y it asks you questions interactively.

mkdir my-app && cd my-app
npm init -y

Install a package

# dependency used by your app at runtime
npm install axios

# dev dependency used only while developing
npm install --save-dev eslint

# run a package once without installing it
npx create-vite@latest my-app
โš  Global vs local: npm install -g puts a package on your whole machine. Avoid it for build tools โ€” a global version can conflict with a project that expects a different one. Use npx or a local install instead.
๐Ÿ’ก Tip: node_modules is huge and machine-specific. It should never be committed โ€” it belongs in .gitignore. Anyone can recreate it with npm install.

03 package.json & Semantic Versioning Intermediate

package.json is the identity card and control panel of a Node project: its name, its dependencies, and the scripts that run it. Understanding this one file unlocks every JavaScript toolchain.

Anatomy of a package.json

{
  "name": "my-app",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "lint": "eslint .",
    "test": "vitest run"
  },
  "dependencies": {
    "axios": "^1.7.9"
  },
  "devDependencies": {
    "vite": "^6.0.0",
    "eslint": "^9.17.0"
  }
}

The three dependency buckets

Semantic versioning (semver)

A version is MAJOR.MINOR.PATCH. Bump MAJOR for breaking changes, MINOR for new features, PATCH for fixes. The symbols in front decide how far npm update may move a package.

^1.4.2
Any 1.x.x โ‰ฅ 1.4.2 (not 2.0.0)
~1.4.2
Any 1.4.x โ‰ฅ 1.4.2 (patch only)
1.4.2
Exactly this version
*
Any version โ€” avoid in real projects
>=1.4.0 <2.0.0
Explicit range
1.4.x
Wildcard patch

The lockfile

package-lock.json records the exact version of every installed package (including transitive ones). Commit it. It is what makes installs reproducible: the same lockfile produces the same tree on every machine and in CI.

Carets are the default: when you npm install axios, npm writes ^1.7.9. That allows minor and patch updates automatically โ€” convenient, but it means fresh installs can drift unless the lockfile is committed.
๐Ÿ’ก Tip: Use "type": "module" to write modern import/export syntax in .js files instead of the older require().

04 Essential npm Commands Beginner

You will use a dozen npm commands constantly. Here they are with what they actually do.

# install everything from package.json
npm install

# clean, exact install for CI (uses the lockfile)
npm ci

# add / remove packages
npm install react react-dom
npm install --save-dev vitest
npm uninstall axios

# update within the allowed semver range
npm update

# see what is outdated
npm outdated

# security audit and automatic fixes
npm audit
npm audit fix

# list installed packages
npm ls

# explain why a package is present
npm why axios

Running scripts

npm run dev
npm run build
npm run lint
npm test        # shorthand for npm run test

Useful extras

# list every available script
npm run

# run a script without the "run" keyword (if no conflict)
npm start

# clear the local cache when installs get weird
npm cache clean --force

# check the registry is reachable
npm ping
install vs ci: use npm install while developing and npm ci in automated builds. npm ci deletes node_modules, installs strictly from the lockfile, and fails if the two disagree โ€” exactly what you want in CI.
๐Ÿ’ก Tip: npm run with no arguments prints every script, which is the fastest way to rediscover a project's commands when you come back to it months later.

05 Bundlers & Vite Intermediate

Modern projects split code across many files and import packages from node_modules. A bundler follows every import and packages it into a few optimized files the browser can load.

Why we bundle

Create and run a Vite project

# scaffold in the current folder
npm create vite@latest . -- --template vanilla

# templates: vanilla, react, vue, svelte, preact, lit, solid
npm create vite@latest my-app -- --template react-ts

cd my-app
npm install
npm run dev       # dev server with HMR

What the scripts do

A minimal Vite config

// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
  server: { port: 5173, open: true },
  build: { outDir: 'dist', sourcemap: true }
})

Other bundlers you will meet

Vite
Fast dev server + Rollup builds โ€” the modern default
webpack
Older, extremely configurable, huge plugin ecosystem
Rollup
Great for libraries, strong tree shaking
esbuild
Written in Go, extremely fast, low-level
Parcel
Zero-config bundler, no setup required
Turbopack
Vercel's Rust bundler for Next.js
๐Ÿ’ก Tip: Hot Module Replacement swaps a changed module in place while keeping app state โ€” so editing a component no longer resets your form fields. That is the difference between a bundler and a live-reload server.

06 Transpilers: TypeScript & Babel Intermediate

Browsers cannot run everything we like to write. A transpiler converts modern or typed source into JavaScript that runs everywhere.

TypeScript in one minute

TypeScript adds static types on top of JavaScript. Types are erased at build time but catch whole classes of bugs while you type. Install it and compile a file:

npm install --save-dev typescript

npx tsc --init          # create tsconfig.json
npx tsc app.ts          # compile one file to app.js
npx tsc --watch          # recompile on every save
// app.ts โ€” types are optional but catch mistakes early
type User = { id: number; name: string; admin?: boolean }

function greet(user: User): string {
  return `Hello, ${user.name}`
}

greet({ id: 1, name: 'Laksh' })   // โœ“
greet({ id: 2 })                  // โœ— error: name is missing
Types vanish at runtime. TypeScript is not an interpreter and adds no checks to the shipped code โ€” it is a compile-time safety net. Vite and most frameworks run it through esbuild/SWC, so it stays fast.

Babel โ€” the compatibility layer

Babel rewrites new JavaScript syntax into older syntax for legacy browsers, and powers JSX and many plugins. Many projects now use Vite or SWC instead, but you will still see Babel in tools and configs.

npm install --save-dev @babel/core @babel/preset-env
// babel.config.json
{
  "presets": [
    ["@babel/preset-env", { "targets": "defaults" }]
  ]
}
๐Ÿ’ก Tip: TypeScript gives you types; Babel gives you compatibility. They solve different problems and can be used together โ€” a combination common in older React setups.

07 Linters & Formatters Beginner

Two tools keep a codebase healthy: a linter finds likely bugs and bad patterns, and a formatter enforces a consistent look. Run both automatically and you stop arguing about style entirely.

ESLint โ€” catch problems

npm install --save-dev eslint
npx eslint --init        # interactive setup
npx eslint .             # lint the whole project
npx eslint . --fix       # auto-fix what it can
// eslint.config.js (flat config)
export default [
  {
    files: ['**/*.js'],
    rules: {
      'no-unused-vars': 'warn',
      'no-console': 'off',
      eqeqeq: ['error', 'always']
    }
  }
]

Prettier โ€” format everything

npm install --save-dev prettier
npx prettier --check .
npx prettier --write .
// .prettierrc
{
  "semi": false,
  "singleQuote": true,
  "printWidth": 100
}

Wire them into scripts and the editor

// package.json
"scripts": {
  "lint": "eslint .",
  "format": "prettier --write ."
}
๐Ÿ’ก Tip: In VS Code, enable Editor: Format On Save and set Prettier as the default formatter. Add the ESLint extension so problems appear inline as you type.
Let Prettier do formatting, let ESLint do correctness. Disable ESLint's stylistic rules and hand all layout decisions to Prettier โ€” otherwise the two tools fight over every line.

08 Browser DevTools Beginner

Every browser ships a built-in development environment. It is the most valuable debugging tool you own โ€” and it is free. Open it with F12, Ctrl+Shift+I, or โŒ˜+Option+I.

The panels that matter

Elements
Inspect the DOM and live-edit HTML & CSS
Console
Run JS, read logs and errors
Sources
Set breakpoints and step through code
Network
See every request, size and timing
Application
Cookies, localStorage, caching, service workers
Performance
Record and find slow frames and tasks
Memory
Find leaks with heap snapshots
Lighthouse
Audit performance, a11y, SEO

Console superpowers

console.log('value:', value)
console.warn('deprecated')
console.error('something broke')

// inspect objects as a table
console.table([{ id: 1, name: 'A' }, { id: 2, name: 'B' }])

// group related logs
console.group('Request')
console.log('url', url)
console.log('status', status)
console.groupEnd()

// time a block of code
console.time('loop')
for (let i = 0; i < 1e6; i++) {}
console.timeEnd('loop')

// select the page element that triggered this call
const el = document.querySelector('.card')
console.log(el)

// inspect computed styles quickly
getComputedStyle(el).display
๐Ÿ’ก Tip: In the Elements panel, click a color swatch to change it live, or toggle a CSS property with the checkbox. Changes are temporary โ€” copy them back into your stylesheet once you like them.
Network throttling: the Network panel can simulate Slow 3G and CPU throttling, so you can feel what your site is like on a mid-range phone without leaving your desk.

09 Debugging JavaScript Intermediate

Printing to the console is a fine start, but a real debugger lets you pause execution and look at everything at that exact moment.

Breakpoints

  1. Open Sources and find your file (or use the Filesystem workspace).
  2. Click a line number to set a breakpoint โ€” the code pauses right before it runs.
  3. Use the controls to step over, step into, step out, or continue.

A hardcoded breakpoint in code

function processOrder(order) {
  const total = order.items.reduce((sum, i) => sum + i.price, 0)
  debugger          // execution pauses here when DevTools is open
  return total
}

Conditional and DOM breakpoints

Reading the error

Uncaught TypeError: Cannot read properties of undefined (reading 'map')
    at render (app.js:42:18)

Read it bottom-up: the file and line after at is where it broke, and the message names the value (undefined) and the property (map). Set a breakpoint there and inspect the variable.

Async gotcha: a stack trace inside a .then() or async function may point at the promise machinery. Use DevTools' Async stack traces (the โ€œAsyncโ€ checkbox in the Call Stack) to see the real caller.
๐Ÿ’ก Tip: Remove your console.log calls once the bug is fixed, or add an ESLint rule to flag stray console statements before they reach production.

10 Environment Variables & Config Intermediate

API URLs, keys and feature flags differ between your laptop and production. Environment variables keep those values out of your source code.

Node / build-time variables

# .env
DATABASE_URL=postgres://localhost:5432/dev
API_SECRET=super-secret-value
// load .env into process.env (Node)
import 'dotenv/config'

console.log(process.env.DATABASE_URL)

Vite exposes variables to the client

Vite only exposes variables prefixed with VITE_, and only those are inlined into the browser bundle.

# .env
VITE_API_URL=https://api.example.com
// accessible in browser code
const base = import.meta.env.VITE_API_URL
const isDev = import.meta.env.DEV
const mode = import.meta.env.MODE

Keep secrets out of Git

# .gitignore
.env
.env.local
.env.*.local
โš  Front-end variables are public. Anything prefixed VITE_ ends up in the JavaScript the browser downloads. Never put API secrets, tokens or passwords there. Secrets belong on a server or a serverless function.
๐Ÿ’ก Tip: Commit a .env.example listing the variable names (with fake values). It documents what collaborators must set without leaking anything real.

11 Testing Tools Intermediate

Tests let you change code with confidence. Start small: a handful of tests around the logic you are most afraid to break.

Unit & component tests with Vitest

npm install --save-dev vitest
npx vitest          # watch mode while developing
npx vitest run      # single run, for CI
// sum.test.js
import { describe, it, expect } from 'vitest'
import { sum } from './sum.js'

describe('sum', () => {
  it('adds two numbers', () => {
    expect(sum(2, 3)).toBe(5)
  })

  it('handles negatives', () => {
    expect(sum(-1, 1)).toBe(0)
  })
})

End-to-end tests with Playwright

npm install --save-dev @playwright/test
npx playwright install
npx playwright test
// e2e/home.spec.js
import { test, expect } from '@playwright/test'

test('home page greets the user', async ({ page }) => {
  await page.goto('http://localhost:5173')
  await expect(page.locator('h1')).toContainText('Laksh')
})
Vitest
Fast unit tests, Vite-native
Jest
Popular all-round test runner
Testing Library
Test components the way users use them
Playwright
Real browser end-to-end tests
Cypress
Developer-friendly e2e with a GUI
MSW
Mock network requests in tests
๐Ÿ’ก Tip: Add "test": "vitest run" to your scripts and run it in CI on every push. A red build on a pull request is far cheaper than a bug in production.

12 Build & Deploy Advanced

The final step: turn source code into an optimized bundle and put it on the internet.

Build and preview locally

npm run build       # outputs an optimized dist/ folder
npm run preview     # serve dist/ locally to test it

What the build does for you

Lazy-load and code-split

// load this module only when needed
const { renderChart } = await import('./chart.js')

// dynamic import in a framework
const Settings = lazy(() => import('./Settings.jsx'))

Deploy the output

Vercel
Connect a Git repo, auto-build and deploy
Netlify
Drag dist/ in or link a repo
GitHub Pages
Free static hosting from a branch
Cloudflare Pages
Global CDN, generous free tier
S3 + CloudFront
Flexible, pay-as-you-go static hosting
Docker
Package a Node server for any platform
# GitHub Pages: publish dist/ with gh-pages
npm install --save-dev gh-pages
npx gh-pages -d dist
โš  Base path: if your site lives at user.github.io/repo/, set base: '/repo/' in the Vite config, or asset URLs will 404. Custom domains use base: '/'.
Continuous deployment: point Vercel, Netlify or Cloudflare at your repository and give it the build command (npm run build) and output directory (dist). Every push then deploys automatically, and every pull request gets a preview URL.
๐Ÿ’ก Tip: Check the Network panel on the deployed site. If the initial JavaScript is over ~200 KB gzipped, look for a large dependency you can lazy-load or swap out.

13 Cheat Sheet & Best Practices Beginner

Commands at a glance

npm init -y
Create package.json
npm install
Install all dependencies
npm ci
Clean install from lockfile
npm i -D pkg
Add a dev dependency
npm run dev
Start the dev server
npm run build
Production bundle
npm run preview
Serve the build locally
npx pkg
Run a package without installing
npm outdated
Show newer versions
npm audit fix
Patch known vulnerabilities
npm why pkg
Explain a dependency
npm run
List all scripts

Best practices

Remember: tools exist to remove friction, not add it. If a tool is slowing you down more than it helps, you are allowed to remove it. Start with the browser, Node and npm โ€” add the rest only when a real problem appears.
๐Ÿ’ก Tip: Bookmark the docs: nodejs.org, npmjs.com, vite.dev, typescriptlang.org, eslint.org and prettier.io. Five minutes reading beats an hour guessing.