Skip to main content
JavaScript · lesson 2 of 24
JavaScript beginner Lesson 2 of 24

Setting Up JavaScript Development Environment

Install Node.js and npm, configure VS Code with the right extensions, and run your first JavaScript program from the terminal.

By SkillByExample · Updated

What You Need

Before writing any code, it helps to understand what each tool does. Node.js is the runtime that lets JavaScript execute outside the browser — on your machine, on servers, anywhere. npm is the package manager bundled with Node.js that gives you access to millions of open-source libraries. VS Code is the editor: it understands JavaScript deeply, offering autocomplete, inline errors, and a built-in terminal.

To follow along with this tutorial series you need:

  1. Node.js (LTS) — the JavaScript runtime for running code outside the browser
  2. npm — the package manager, bundled with Node.js automatically
  3. VS Code — the most widely used editor for JavaScript development
  4. A terminal (PowerShell on Windows, Terminal on macOS/Linux)

Installing Node.js

Node.js has two release lines: Current (latest features) and LTS (Long-Term Support). For learning and for any serious project, always choose LTS — it receives security patches for 30 months and is what the broader ecosystem targets.

Go to nodejs.org and download the LTS installer for your operating system. The installer also installs npm.

After installation, verify both are available:

node --version
npm --version
$ node --version
v24.18.1
$ npm --version
11.1.0

Node 24 is an LTS line, and npm 11 ships with Node 24. Your numbers will differ with the release you installed. What matters is that both commands print a version instead of command not found. If one fails, close and reopen the terminal so it picks up the updated PATH.

As you work on more projects you’ll eventually encounter situations where different projects require different versions of Node. A version manager solves this cleanly — it lets you install multiple Node versions side by side and switch between them with one command.

# With nvm installed:
nvm install --lts           # install latest LTS
nvm use --lts               # activate it
nvm install 22              # install a specific version
nvm use 22                  # switch to it

Installing VS Code

Download from code.visualstudio.com. It’s free and available for Windows, macOS, and Linux.

Essential Extensions

VS Code’s power comes from its extension ecosystem. These five extensions cover the essentials for JavaScript development — linting, formatting, path completion, and better error visibility. Install them from the Extensions panel (Ctrl+Shift+X / Cmd+Shift+X):

ExtensionPurpose
ESLintCatches code quality issues and style violations
Prettier - Code formatterOpinionated auto-formatter — formats on save
Path IntelliSenseAutocompletes file paths in import statements
JavaScript (ES6) code snippetsUseful shorthands for common patterns
Error LensInline error and warning display

These settings make the editor work the way most JavaScript teams expect. The most important one is formatOnSave — it means you never have to think about formatting; save the file and Prettier fixes it automatically.

Open settings (Ctrl+,) and add these to your settings.json:

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.tabSize": 2,
  "editor.detectIndentation": false,
  "javascript.updateImportsOnFileMove.enabled": "always"
}

The Browser Developer Tools Console

You don’t always need Node.js. For quick experiments and debugging, your browser’s built-in console is always available — no setup, no files. It’s the fastest way to test a snippet or explore how a built-in method works.

  1. Open Chrome, Edge, or Firefox
  2. Press F12 (or right-click → Inspect)
  3. Click the Console tab
  4. Type JavaScript directly and press Enter
// Try this in the browser console
const nums = [1, 2, 3, 4, 5];
const doubled = nums.map(n => n * 2);
console.log(doubled);

The output below is not from a browser: a browser console can’t be captured from a terminal. It is the same four lines saved as console.js and run with Node:

$ node console.js
[ 2, 4, 6, 8, 10 ]

The first line is only a comment, so the code needs no changes to run in Node. Each element is doubled, and the result is a new array. A browser console prints the same values but formats the array its own way, usually as an expandable entry. The browser console is perfect for exploring DOM APIs, testing snippets, and debugging live pages.

Your First Node.js Program

Running JavaScript through Node.js is the foundation for everything in this series. It lets you execute any .js file from your terminal, access the filesystem, and use npm packages. Create a new folder and open it in VS Code:

mkdir js-practice && cd js-practice
code .

Create a file called hello.js:

// hello.js
// A simple function that returns a greeting string
function greet(name) {
  return `Hello, ${name}! Welcome to JavaScript.`;
}

const names = ['Alice', 'Bob', 'Charlie'];

// forEach iterates the array and runs the callback for each element
names.forEach(name => {
  console.log(greet(name));
});

Run it from the terminal inside VS Code (`Ctrl+“ to open it):

$ node hello.js
Hello, Alice! Welcome to JavaScript.
Hello, Bob! Welcome to JavaScript.
Hello, Charlie! Welcome to JavaScript.

forEach calls the arrow function once per name, in array order, and each call prints one line. The comment lines in the file produce no output.

Initializing a Project with npm

Most real projects depend on external packages — UI libraries, utility functions, testing frameworks. The package.json file is how Node.js tracks those dependencies and lets anyone clone your project and install everything they need with one command.

npm init -y

This creates a package.json with default values; the -y flag accepts every prompt. npm 11 also writes keywords, author, license and "type": "commonjs". Below is the file after replacing its scripts block by hand with start and dev scripts (npm init only adds a placeholder test script):

{
  "name": "js-practice",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "node --watch index.js"
  }
}

The scripts block lets you define shortcuts that any contributor can run without knowing the underlying commands:

npm run start   # runs "node index.js"
npm run dev     # runs with --watch, auto-restarts on file change

node --watch was added in Node 18 and replaces the popular nodemon package for simple cases.

Running a Script with a Dependency

Installing a package and using it is the core npm workflow. Once you run npm install, the package is downloaded into node_modules/ and recorded in package.json — anyone who clones the repo can run npm install to get the exact same versions.

npm install chalk

Now use it in a script:

// colorful.js
// chalk adds ANSI color codes to terminal output
import chalk from 'chalk';

console.log(chalk.green('Success:'), 'Server started');
console.log(chalk.red('Error:'), 'Connection refused');
console.log(chalk.yellow('Warning:'), 'Disk usage above 80%');

For ES module imports (import syntax) you need to add "type": "module" to package.json:

{
  "type": "module"
}
node colorful.js

You’ll see colored output in the terminal — chalk is a tiny but very practical package.

Project Structure Conventions

Keeping a predictable folder structure matters once a project grows beyond a handful of files. The most important rule: never commit node_modules/ — it can contain thousands of files and is always reproducible from package.json. Here is js-practice after npm install chalk, with the application code moved into src/index.js, listed with the tree utility (brew install tree / apt install tree) from the parent folder:

$ tree -a -L 2 --noreport js-practice
js-practice
├── .gitignore
├── node_modules
│   ├── .package-lock.json
│   └── chalk
├── package-lock.json
├── package.json
└── src
    └── index.js

node_modules/ holds the installed packages. Here that is only chalk plus npm’s own bookkeeping file, but a real project can have thousands of entries, so never commit it. package-lock.json records the exact versions installed; commit it so every clone gets the same ones. Your code lives in src/.

Your .gitignore should always include these three lines:

$ cat js-practice/.gitignore
node_modules/
.env
dist/

.env holds local secrets such as API keys, and dist/ holds build output that can be regenerated, so neither belongs in Git.

Practice

  1. Change hello.js so each greeting is numbered, starting at 1, and add 'Dana' to the array. forEach passes the element’s index as the callback’s second argument.
Solution
// hello.js
function greet(name, position) {
  return `${position}. Hello, ${name}! Welcome to JavaScript.`;
}

const names = ['Alice', 'Bob', 'Charlie', 'Dana'];

// forEach passes the index as the callback's second argument
names.forEach((name, index) => {
  console.log(greet(name, index + 1));
});
$ node hello.js
1. Hello, Alice! Welcome to JavaScript.
2. Hello, Bob! Welcome to JavaScript.
3. Hello, Charlie! Welcome to JavaScript.
4. Hello, Dana! Welcome to JavaScript.

Indexes start at 0, so index + 1 produces the human-friendly numbering. Adding a name to the array is all it takes to get a fourth line, because forEach visits every element.

  1. Move the browser-console snippet into a file called doubled.js. Add a second map that squares each number, and print nums last. Before running it, predict whether nums has changed.
Solution
// doubled.js
const nums = [1, 2, 3, 4, 5];
const doubled = nums.map(n => n * 2);
const squared = nums.map(n => n * n);
console.log(doubled);
console.log(squared);
console.log(nums);
$ node doubled.js
[ 2, 4, 6, 8, 10 ]
[ 1, 4, 9, 16, 25 ]
[ 1, 2, 3, 4, 5 ]

map builds a new array and leaves the original alone, so nums prints unchanged. The same code runs in the browser console and in Node; only the array formatting differs.

  1. Run npm init -y, then edit the scripts block in package.json so npm run start runs hello.js and a new script, doubled, runs doubled.js. Run both.
Solution
"scripts": {
  "start": "node hello.js",
  "doubled": "node doubled.js"
}
$ npm run start

> js-practice@1.0.0 start
> node hello.js

1. Hello, Alice! Welcome to JavaScript.
2. Hello, Bob! Welcome to JavaScript.
3. Hello, Charlie! Welcome to JavaScript.
4. Hello, Dana! Welcome to JavaScript.
$ npm run doubled

> js-practice@1.0.0 doubled
> node doubled.js

[ 2, 4, 6, 8, 10 ]
[ 1, 4, 9, 16, 25 ]
[ 1, 2, 3, 4, 5 ]

npm prints the package name, version, script name and the exact command before running it. Any key you add under scripts becomes something you can run with npm run <key>.

  1. Install chalk and add a fourth line to colorful.js that prints a blue Info: label. Recent npm versions write "type": "commonjs" into package.json during npm init -y. If yours did, run colorful.js once before you change it, then change the value to "module" and run it again.
Solution
// colorful.js
import chalk from 'chalk';

console.log(chalk.green('Success:'), 'Server started');
console.log(chalk.red('Error:'), 'Connection refused');
console.log(chalk.yellow('Warning:'), 'Disk usage above 80%');
console.log(chalk.blue('Info:'), 'Listening on port 3000');

With "type": "commonjs" (Node v24.18.1, npm 11.1.0):

$ node colorful.js
(node:7532) Warning: Failed to load the ES module: S:\js-practice\colorful.js. Make sure to set "type": "module" in the nearest package.json file or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
S:\js-practice\colorful.js:2
import chalk from 'chalk';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (node:internal/modules/cjs/loader:1804:18)
    at Module._compile (node:internal/modules/cjs/loader:1845:20)
    at Object..js (node:internal/modules/cjs/loader:2002:10)
    at Module.load (node:internal/modules/cjs/loader:1594:32)
    at Module._load (node:internal/modules/cjs/loader:1396:12)
    at wrapModuleLoad (node:internal/modules/cjs/loader:255:19)
    at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
    at node:internal/main/run_main_module:33:47

Node.js v24.18.1

After changing it to "type": "module":

$ node colorful.js
Success: Server started
Error: Connection refused
Warning: Disk usage above 80%
Info: Listening on port 3000
$ npm ls
js-practice@1.0.0 S:\js-practice
└── chalk@6.0.1

"commonjs" tells Node to treat .js files as CommonJS, where import is a syntax error. In a terminal the labels are coloured; this captured output is plain text, so the colour codes are missing. npm ls confirms chalk is the only dependency.

What’s Next

With your environment ready, the next tutorial dives into variables — var, let, and const — and the scoping rules that trip up beginners and experienced developers alike.

Frequently Asked Questions

Which version of Node.js should I install?
Always install the current LTS (Long-Term Support) version. It receives security and bug fixes for 30 months, making it the safe choice for any project.
Do I need Node.js to write JavaScript?
Not for browser JavaScript — you can write JS and open it in a browser. But Node.js is essential for running scripts locally, using npm packages, and building modern frontend tooling.
Can I use a different editor instead of VS Code?
Yes — WebStorm is a popular paid alternative with deep JavaScript support. Neovim with LSP plugins also works well. That said, VS Code has the broadest ecosystem of JS/TS extensions and is free.