How to Set Up VS Code Properly for Web Development

Complete VS Code setup guide for HTML, CSS, and JavaScript with essential extensions, settings, debugging, productivity tips, and best practices.

A fresh install of VS Code is a blank canvas: no formatting, no linting, no live preview, and none of the autocomplete you've seen in tutorials. That gap between "installed" and "productive" trips up a lot of developers, and closing it takes about fifteen minutes once you know which settings and extensions actually matter.

How to Set Up VS Code Properly for Web Development

This guide, put together from the setup we rely on day to day, covers a complete VS Code environment for HTML, CSS, and JavaScript development: the essential extensions, the settings.json values worth changing, the built-in features most people never discover, and a debugging workflow that doesn't require leaving the editor. No 50-extension lists, no filler. Just a configuration you can copy, paste, and start using today.

Why VS Code Needs Configuration

VS Code ships minimal by design. Microsoft built it as a lightweight, extensible code editor rather than a fully loaded IDE, which keeps it fast but puts the setup work on you. Left unconfigured, a few problems show up almost immediately:

  • HTML tags don't auto-close, so you're typing </div> by hand every time
  • CSS and JS formatting drifts, mixed tabs and spaces, inconsistent quote styles, messy diffs in version control
  • There's no live preview, so you're alt-tabbing to a browser and refreshing manually
  • JavaScript errors surface only when the page actually breaks, instead of while you're typing
  • File paths in imports and src attributes have to be typed from memory, typos included

None of this is a flaw in VS Code, it's just work the editor leaves to you. Extensions close most of these gaps; the rest come from settings and built-in features most people never turn on. That's the order this guide follows: extensions first, then the settings that make them behave, then the native capabilities sitting underneath both.

Quick note! Everything here works identically on Windows, Mac, and Linux. VS Code is free and cross-platform, so there's nothing OS-specific to worry about.

Essential Extensions

The marketplace has tens of thousands of extensions, and most of them you don't need. A bloated extension list slows the editor down and creates formatter conflicts, two extensions fighting to auto-format the same file is one of the most common early frustrations, and it's rarely obvious which one is winning. These seven cover the vast majority of HTML, CSS, and JavaScript work, and each earns its place for a specific reason:

Prettier — Code Formatter

Formats HTML, CSS, and JavaScript automatically on save, so indentation, quote style, and line breaks stay consistent without manual cleanup. It becomes valuable the moment more than one person touches a file, or the moment you reopen your own code a week later and don't want to relitigate style choices. This is the single highest-leverage extension on the list; the full setup is in the Recommended Settings section below.

ESLint

Analyzes your JavaScript as you type and flags problems, undeclared variables, unreachable code, missing brackets, before you ever run the page. It becomes useful the moment your JavaScript grows past a few dozen lines, since that's roughly where typos stop being obvious on sight. In theory ESLint checks code quality and Prettier handles formatting, and they shouldn't overlap. In practice, older ESLint configs sometimes enable stylistic rules, spacing, quote style, that duplicate what Prettier already does, and the two will "fight" over the same line on every save. If you see a line flip-flop or get flagged right after Prettier just formatted it, that's the symptom; the fix is adding eslint-config-prettier to your ESLint config, which turns off every ESLint rule that overlaps with Prettier's job.

Live Server

Spins up a local dev server and refreshes the browser automatically on save. For plain HTML/CSS/JS projects without a build tool, this replaces manual refreshing entirely. Right-click any HTML file and choose "Open with Live Server" to start.

GitHub Copilot

An AI pair programmer that suggests whole lines or blocks of code as you type, based on the surrounding context. It's most valuable for boilerplate you've written a hundred times before, form markup, fetch calls, repetitive CSS patterns, less valuable for logic that actually requires thinking through, where accepting a plausible-looking suggestion without reading it is the most common way it introduces a bug. A GitHub account is all that's required to start: the free tier includes a monthly allotment of completions and chat requests with no card on file, and paid plans exist for developers who exceed that allotment regularly.

Error Lens

Takes the errors and warnings VS Code already detects and prints them inline, right at the end of the offending line, instead of tucking them into the Problems panel. It's most useful while actively writing new code, when you want to catch a typo the instant it happens rather than after a save. One trade-off worth knowing: on a file with a lot of unresolved warnings, or alongside Copilot's inline suggestion text, the line can get visually noisy, both render as grey text past the end of your code. If that happens, the errorLens.enabledDiagnosticLevels setting lets you restrict Error Lens to errors only and drop the warning-level noise.

Path Intellisense

Autocompletes file paths as you type them inside src, href, and import statements. It earns its keep on any project with more than a couple of folders, once you're importing across /src/css and /src/assets instead of everything sitting next to your HTML file, typing a relative path from memory is where most broken links and silent 404s actually come from.

EditorConfig for VS Code

Reads a plain-text .editorconfig file at your project root and applies indentation, charset, and line-ending rules automatically, before Prettier even runs. Where this earns its place over relying on Prettier alone: .editorconfig is a plain-text format understood by editors far beyond VS Code, so it's the layer that keeps a teammate on a completely different editor indenting the same way you do. Covered in more detail in the checklist section.

Info: You don't need to install all seven in one sitting. Start with Prettier, ESLint, and Live Server, they solve the most common friction, and add the rest as you notice a real gap.

Recommended Settings

Installing Prettier alone doesn't do anything until VS Code knows to use it. Open your settings.json directly with Ctrl+Shift+P (or Cmd+Shift+P on Mac), type "Open User Settings (JSON)," and add this block:

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[html]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[css]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "editor.tabSize": 2,
  "editor.insertSpaces": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "editor.quickSuggestions": {
    "strings": true
  }
}

A few of these are worth calling out. The [html], [css], and [javascript] blocks explicitly assign Prettier per language, this matters because another installed extension can silently claim the default formatter slot for a specific file type, and format-on-save will look like it's failing when really the wrong formatter is running. source.fixAll.eslint tells ESLint to auto-fix what it safely can on every save, alongside Prettier's formatting pass, and using "explicit" rather than true means it only runs on an actual keyboard save rather than every autosave tick. And quickSuggestions.strings is what makes Path Intellisense and CSS class suggestions actually appear inside quoted attribute values; without it, VS Code treats string contents as plain text.

Notice HTML tag auto-closing isn't in that block. That's deliberate: html.autoClosingTags is already true by default, so setting it again does nothing. A related setting, javascript.autoClosingTags, shows up in a lot of older tutorials and looks like the JS equivalent, but it actually controls closing tags in JSX (React syntax) and has no effect in a plain .js file. Adding it to a non-React project is harmless but pointless, which is exactly the kind of copy-pasted setting worth cutting when you find it in your own config.

For settings that should travel with the project itself rather than living only on your machine, add a .prettierrc at the project root:

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 80
}

Commit this file. Because it's plain text sitting in your repo, anyone who opens the project, a collaborator, a contributor, or you on a different machine, gets identical formatting the moment they hit save, regardless of their personal editor settings.

Productivity Features

Extensions and settings handle formatting, linting, and previewing. The features below are different: they ship inside VS Code itself, need no configuration to turn on, and are what actually speed up the hour-to-hour work of writing and navigating code. Most beginners learn these by accident, months in, after watching someone else's screen. Learning them on purpose is faster.

IntelliSense

VS Code's native IntelliSense provides autocomplete, parameter hints, and quick documentation for HTML tags, CSS properties, and JavaScript, all without an extension. Trigger it manually with Ctrl+Space if a suggestion list doesn't pop up on its own.

Emmet abbreviations

Emmet ships built into VS Code and expands short abbreviations into full markup. Type div.card>h2+p inside an HTML file, and as you type, the expansion appears in the autocomplete dropdown; press Enter or Tab to accept it, and it becomes a div with class card containing an h2 and a p. It works for CSS too: m10-20 expands to margin: 10px 20px;. It's worth writing this precisely because it's a common source of confusion: pressing Tab at an arbitrary cursor position doesn't expand an abbreviation unless the suggestion is already highlighted, VS Code deliberately stopped binding Tab directly to Emmet years ago, since Tab is also how you indent. If you want Tab to expand an abbreviation from anywhere, not just from the dropdown, add "emmet.triggerExpansionOnTab": true to settings.json. Either way, once it clicks, typing repetitive markup by hand starts to feel unnecessary.

Command Palette

Press Ctrl+Shift+P (Cmd+Shift+P on Mac) and type what you want to do in plain language, "format document," "toggle word wrap," "change language mode." It runs virtually every command VS Code has, including ones with no assigned keyboard shortcut at all. It becomes the fastest way to do anything you do rarely, renaming a file's language mode, reloading the window after a settings change, running a specific ESLint fix, without hunting through menus to find it.

Multi-cursor editing

Alt+Click (Option+Click on Mac) drops an extra cursor anywhere you click, and typing then happens at every cursor simultaneously. For editing the same pattern across several lines at once, updating five similar attribute values, adding a class to a run of list items, this replaces five separate edits with one. It's genuinely useful the moment you're about to make the same small change more than twice in a row.

Rename Symbol

Place your cursor on a variable, function, or class name and press F2. Type the new name and every usage across the file, and in supported languages across the whole project, updates automatically. This is worth using instead of find-and-replace specifically because it understands scope: renaming a local variable called data won't touch an unrelated data variable in a different function. Find-and-replace can't tell the difference; Rename Symbol can.

Go to Definition & Find All References

F12 jumps straight to where a function or variable was originally defined. Shift+F12 does the opposite: it lists every place a symbol is used across the project. Together they answer the two questions that come up constantly once a codebase has more than one file, "where does this actually come from" and "what will break if I change it." Alt+F12 (Option+F12 on Mac) is worth knowing too: it peeks the definition inline, in a small embedded panel, without navigating away from where you're currently reading.

Code Actions

When VS Code underlines something, a missing import, an unused variable, a fixable ESLint warning, a small lightbulb appears next to it. Click the lightbulb, or press Ctrl+. (Cmd+. on Mac) with your cursor on that line, and VS Code offers fixes it can apply automatically. It's the fastest way to clear a warning without leaving the keyboard, and it's where a lot of ESLint's auto-fixable rules surface if you'd rather review a fix before accepting it than let source.fixAll.eslint apply it silently on save.

Integrated terminal

Press Ctrl+` to open a terminal inside VS Code, pre-scoped to your project's root directory. Running npm install, starting a dev server, or checking Git status without switching to a separate terminal app keeps your attention on the editor instead of splitting it across windows. It matters most the moment a project stops being pure HTML/CSS/JS and picks up any kind of build step or package manager, at that point you're in a terminal constantly, and a separate terminal window is one more thing pulling focus away from your code.

Git integration

Git support is built in. The Source Control panel (Ctrl+Shift+G) shows changed files, lets you stage and commit without touching the command line, and inline gutter indicators mark added, modified, and deleted lines directly next to your code as you edit. The gutter markers alone are worth knowing about even if you commit from the terminal: a thin colored bar in the left margin telling you exactly which lines changed since your last commit is faster to scan than running git diff for a quick sanity check before saving.

Debugging Setup

Sprinkling console.log() through your code works, but it's slow and it means digging through the browser console for output. VS Code has a built-in JavaScript debugger that sets real breakpoints, steps through code line by line, and lets you inspect variable values directly in the editor, and unlike older setups, it requires no extension. The debugger has shipped inside VS Code since version 1.46 and fully replaces the old Debugger for Chrome and Debugger for Firefox extensions, which are deprecated and no longer necessary.

To set it up, press Ctrl+Shift+D to open the Run and Debug panel, click "create a launch.json file," choose Chrome (or Edge) as the environment, and use this configuration:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "Launch Chrome against localhost",
      "url": "http://localhost:5500",
      "webRoot": "${workspaceFolder}/src"
    }
  ]
}

Click to the left of any line number in a JS file to set a breakpoint, then hit the green play button. Execution pauses at that exact line, and hovering over a variable shows its current value directly in the editor. This earns its place over console.log() the moment you're debugging something intermittent, a value that's correct nine times and wrong the tenth, since you can inspect the full call stack and every variable in scope at the exact moment it went wrong, instead of guessing which values to print in advance. For a quicker one-off check, skip the launch.json entirely and run the "Debug: Open Link" command from the Command Palette to attach the debugger to any URL.

One thing worth knowing if you've copied a launch.json from an older tutorial or a different project: you may see "type": "pwa-chrome" instead of "type": "chrome". They're functionally the same debugger, but pwa-chrome is the older internal name, and current VS Code will flag it with a warning suggesting you switch to chrome. It's harmless to leave as-is, but it's a signal the config predates a naming cleanup, and it's worth updating if you're setting one up fresh.

Common error: If breakpoints aren't hit, confirm the url in launch.json matches the port Live Server is actually running on, shown in the bottom-right corner of VS Code once it starts. A close second: breakpoints set in a file that isn't the one actually being served, for example editing a copy in /dist while Live Server serves from /src, will sit there permanently unverified, shown as a hollow grey circle instead of solid red.

Project Structure

Once a project grows past a single HTML file, folder structure stops being cosmetic. It changes how fast Ctrl+P (Quick Open) finds the file you're after, how noisy project-wide search (Ctrl+Shift+F) gets, and how quickly a teammate can guess where something lives without asking. A flat folder with forty files sitting side by side means every fuzzy-file search returns a wall of near-matches; splitting by purpose means Ctrl+P plus a couple of typed letters usually gets you there in one shot. Here's a structure that scales cleanly for small to medium web projects:

Folder / File Purpose
/src All source code — HTML, CSS, and JS files
/src/assets Images, fonts, icons
/src/css Stylesheets, split by component on larger projects
.editorconfig Baseline indentation and charset rules, applied before Prettier runs
.prettierrc Shared formatting rules for the whole project
.vscode/settings.json Project-specific editor settings applied automatically to anyone opening this folder

That last one is the file most people never discover. A .vscode folder containing a settings.json applies those settings automatically to anyone who opens that specific project folder, without touching their personal global settings. In practice, this is what actually closes the loop on "why does formatting look different on my machine": a new contributor clones the repo, opens it in VS Code, and format-on-save with Prettier as default formatter is already active for them, no onboarding doc, no "step 4: configure your editor" required.

Warning: Don't put API keys, tokens, or personal file paths inside .vscode/settings.json if you're committing it to a public repository. It's meant for shared editor behavior, not secrets.

Common VS Code Mistakes to Avoid

A handful of issues account for most of the setup friction people run into. Knowing them in advance saves the debugging-your-editor detour:

  • Installing two formatters for the same language. Prettier plus a language-specific formatter extension both claiming the default formatter slot is the single most common reason "format on save isn't working." Only one formatter can be default per language, check with "Format Document With" if you're unsure which one is actually active.
  • Letting ESLint and Prettier argue over the same rule. If a line changes right after saving, or gets flagged the instant Prettier reformats it, that's a stylistic ESLint rule duplicating Prettier's job. Add eslint-config-prettier to your ESLint config to turn those specific rules off.
  • Assuming a personal setting will follow the project. Global user settings live on your machine only. A collaborator, or you on a different laptop, won't have them unless they're committed to the project via .vscode/settings.json, .prettierrc, and .editorconfig.
  • Copy-pasting a setting without knowing what it does. javascript.autoClosingTags is the clearest example: it shows up in a lot of older configs, looks like it should auto-close HTML tags in JS files, and actually only affects JSX. It's not harmful, just noise, but it's worth understanding a setting before adding it rather than after.
  • Installing extensions faster than you can evaluate them. A dozen barely-used extensions is a slower, less predictable editor. If you haven't opened an extension's settings or noticed it working in weeks, it's a candidate to disable.
  • Debugging with a stale or mismatched port. A launch.json pointed at localhost:5500 while Live Server actually started on 5501, because 5500 was already in use, is the most common reason breakpoints silently never hit. The active port is always shown in VS Code's status bar once Live Server starts.

5-Minute VS Code Setup

Everything above, condensed into the order to actually do it in.

1. Install five extensions (2 minutes) — search the Extensions panel (Ctrl+Shift+X) for each:

  • Prettier — Code Formatter (esbenp.prettier-vscode)
  • ESLint (dbaeumer.vscode-eslint)
  • Live Server (ritwickdey.LiveServer)
  • Error Lens (usernamehw.errorlens)
  • EditorConfig for VS Code (EditorConfig.EditorConfig)

Add Path Intellisense (christian-kohler.path-intellisense) and GitHub Copilot (GitHub.copilot) afterward, once you've felt the gap each one fills.

2. Paste the settings block (1 minute) — Ctrl+Shift+P → "Open User Settings (JSON)" → paste the full block from the Recommended Settings section above.

3. Drop two files at your project root (1 minute):

# .editorconfig
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

...plus a .prettierrc with your formatting preferences, from the Recommended Settings section.

4. Commit the project-level config (1 minute) — .prettierrc, .editorconfig, and .vscode/settings.json all get committed to the repo, so the setup travels with the project instead of living only on your machine.

5. Set up the folder structure/src for source files, /src/assets for media, /src/css for stylesheets, config files at the root. Configure launch.json for the built-in debugger the first time you actually need to set a breakpoint, there's no reason to do it before then.

Frequently Asked Questions

Does installing too many extensions slow VS Code down?

Yes. Each active extension consumes some memory and CPU, and a handful of poorly optimized ones can make typing feel laggy. Stick to extensions you use regularly, and check the Extensions panel occasionally to disable anything installed once and forgotten.

Is Live Server a replacement for a real backend server?

No. Live Server is built for static HTML/CSS/JS during development, it doesn't handle server-side logic, databases, or APIs. Once a project needs those, you'll move to something like Node.js with Express, but for front-end development and learning, Live Server is exactly the right tool.

Do I still need the Debugger for Chrome extension?

No, and installing it isn't recommended. VS Code has included a built-in JavaScript debugger since version 1.46, and it fully replaces the old Debugger for Chrome and Debugger for Firefox extensions, both of which are deprecated. Everything in the Debugging Setup section above works with zero extensions installed.

My format-on-save isn't working even though Prettier is installed. What's wrong?

This is almost always a default formatter conflict. Open the file, right-click anywhere in it, choose "Format Document With," and check whether Prettier is actually listed and selected for that file type. If a different extension has claimed the default formatter slot, Prettier won't run even though it's installed and enabled.

Does this setup carry over to React or other frameworks?

Mostly, yes. Prettier, ESLint, EditorConfig, and the formatting settings all carry over directly. Live Server is specific to plain HTML projects though, so once you move to React, Vue, or a similar framework, you'll use that framework's own dev server instead, typically started with a terminal command like npm run dev.

Is any of this setup paid?

VS Code itself is free, and Prettier, ESLint, Live Server, Error Lens, Path Intellisense, and EditorConfig are all free extensions with no usage caps. GitHub Copilot is the one exception worth qualifying: it needs a GitHub account, but that account gets a genuinely usable free tier, a monthly allotment of completions and chat requests, with no card required. Paid plans exist for developers who exceed that allotment regularly. Everything else in this guide costs nothing, ever.

Conclusion

A properly configured editor doesn't make you a better developer overnight, but it removes friction that otherwise eats into your actual thinking time. Format-on-save means you stop manually fixing indentation. Live Server means you stop alt-tabbing every ten seconds to check a change. Rename Symbol and Go to Definition mean you stop scrolling through files by hand to trace where something comes from. A committed .prettierrc and .editorconfig mean the project looks the same no matter who opens it, or when.

None of it needs to happen at once. The five-minute setup above covers the extensions and settings that solve the most common friction immediately; the navigation shortcuts and debugging workflow are worth picking up as you actually feel the gap they fill. That's the entire idea: less fighting with tools, more time spent actually building things.

Post a Comment