Working seriously with React requires a small ecosystem of tools: a JavaScript runtime outside the browser (Node.js), a package manager (npm), a build tool that transforms your code and gives you a fast development server (Vite), a properly configured editor, and a browser extension for inspecting components. In this lesson you'll set up that environment from start to finish and create the CicloUrbano project you'll use throughout the course. By the end you'll have an app running in your browser and you'll understand what every generated file does.

Contents

  1. Prerequisites: Node.js and npm
  2. The editor and its extensions
  3. Creating the CicloUrbano project with Vite
  4. A tour of the folder structure
  5. The npm scripts: dev, build, preview
  6. Hot Module Replacement
  7. React DevTools in the browser
  8. Why Vite and not Create React App

  1. Prerequisites: Node.js and npm

React is written with a syntax (JSX) that browsers don't understand directly, and it's distributed in packages you need to download. Both of those require Node.js, the runtime that lets you execute JavaScript outside the browser, and npm (Node Package Manager), which installs alongside Node.

Which version to install

Always use an LTS (Long Term Support) version: these are the even-numbered releases (20, 22, 24...) with extended support, and the ones tooling treats as safe. Vite requires Node 20.19 or later. Download it from nodejs.org, or better yet, install a version manager like nvm (macOS/Linux) or fnm (cross-platform), which lets you keep several versions and switch between them per project.

Checking that everything's in place

Open a terminal and run:

node --version
npm --version

Expected output (the exact numbers will vary, but this is the format):

v22.14.0
10.9.2

If the command isn't recognized, Node isn't installed or isn't on the PATH; restart your terminal after installing it. If your Node version is below 20, update it before continuing — otherwise you'll run into confusing errors later on.

A note on package managers

Manager Install command Notes
npm npm install Comes with Node. It's what we'll use throughout the course
pnpm pnpm install Faster, and saves disk space by sharing dependencies across projects
yarn yarn A long-standing alternative, still widely used in existing projects

All three solve the same problem, and the commands are nearly interchangeable. We'll use npm because it doesn't require installing anything extra.

  1. The editor and its extensions

The recommended editor is Visual Studio Code, free and with the best support for React. These extensions make a real difference day to day:

Extension What it's for
ESLint Flags errors and bad practices as you type (misused hooks, unused variables, missing key)
Prettier Formats your code automatically on save — no more arguing about quotes and indentation
ES7+ React/Redux/React-Native snippets Shortcuts like rafce that generate the skeleton of a function component
Auto Rename Tag Renaming an opening JSX tag automatically renames its closing tag
Error Lens Shows the error message right on the line, without hovering over it

One setting well worth turning on is format-on-save. In VS Code, Ctrl+, → search for "format on save" → check the box. Or set it directly in .vscode/settings.json inside the project:

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  }
}

  1. Creating the CicloUrbano project with Vite

Vite (pronounced "veet," French for "fast") is today's standard build tool for React projects. It does two things: during development it spins up an almost-instant server that serves your code, and for production it generates the optimized files you'll upload to your host.

Go to the folder where you keep your projects and run:

npm create vite@latest ciclourbano -- --template react

Let's break that command down, because the double dashes trip a lot of people up:

  • npm create vite@latest downloads and runs Vite's scaffolding wizard at its latest version, without installing it permanently.
  • ciclourbano is the name of the project folder.
  • The -- separates npm's arguments from the wizard's arguments. Without it, npm would try to interpret --template as one of its own options.
  • --template react selects the React template with JavaScript. (If you wanted TypeScript, it would be react-ts — you'll see that in lesson 10-04, but this course uses JavaScript.)

Expected output:

Scaffolding project in /home/your-username/projects/ciclourbano...

Done. Now run:

  cd ciclourbano
  npm install
  npm run dev

Follow those three commands:

cd ciclourbano
npm install

npm install reads package.json, downloads the dependencies into the node_modules/ folder, and creates package-lock.json. It takes anywhere from a few seconds to a minute, depending on your connection:

added 152 packages, and audited 153 packages in 12s
found 0 vulnerabilities

Then start the development server:

npm run dev
  VITE v7.0.0  ready in 187 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

Open http://localhost:5173/ in your browser: you'll see the Vite + React welcome page with a counter. Leave this process running in its terminal while you work; to stop it, press Ctrl+C.

If port 5173 is already taken, Vite will use 5174, 5175, and so on. Always check the URL the console prints.

  1. A tour of the folder structure

Here's what Vite has generated:

ciclourbano/
├── node_modules/          <- downloaded dependencies (never touch this, never commit it to git)
├── public/                <- static files served as-is
│   └── vite.svg
├── src/                   <- YOUR code: this is where you'll always work
│   ├── assets/
│   │   └── react.svg
│   ├── App.css
│   ├── App.jsx            <- the app's root component
│   ├── index.css          <- global styles
│   └── main.jsx           <- JavaScript entry point
├── .gitignore
├── eslint.config.js
├── index.html             <- the actual HTML page the browser serves
├── package.json
├── package-lock.json
├── README.md
└── vite.config.js

index.html: the real starting point

In a Vite project, the HTML isn't hidden away inside the configuration — it's a top-level file, and it's the real entry point.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

Two lines matter most:

  • <div id="root"></div> is the empty container where React will mount the whole application. Everything you'll see on screen ends up inside that div.
  • <script type="module" src="/src/main.jsx"> loads your code as an ES module. It's the thread that connects the HTML to React.

Since it's your project, go ahead and personalize it now:

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>CicloUrbano — Urban Bike Rental</title>
  </head>

src/main.jsx: where React starts

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App.jsx';

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>
);

This is the file that connects React to the div#root in the HTML. We'll walk through it line by line in the next lesson; for now, just remember that this is where everything starts.

The rest of the pieces

File / folder What it's for
src/App.jsx Root component. The entire CicloUrbano component tree hangs off it
src/index.css Global styles: base typography, colors, margin reset
src/App.css Styles for the App component (you'll see better strategies for this in Module 2)
src/assets/ Images and resources you import from your code; Vite optimizes them and adds a hash when it builds
public/ Resources served as-is, unprocessed, at the site root: public/logo.png is served as /logo.png. Use it for favicon.ico, robots.txt, or downloads
package.json Project name, dependencies and runnable scripts
package-lock.json Exact installed versions. Committed to git so the whole team installs the same thing
vite.config.js Vite configuration (plugins, aliases, port, proxy)
eslint.config.js Static code analysis rules
.gitignore List of what git should ignore; includes node_modules/ and dist/
node_modules/ Downloaded dependencies. Huge, regenerated with npm install, and never committed to git

The key difference between src/assets/ and public/: what's in assets goes through the build process (it's optimized, renamed with a hash for good caching, and removed if it's unused); what's in public is copied untouched and its name never changes.

vite.config.js

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()]
});

It's minimal on purpose. The @vitejs/plugin-react plugin is what teaches Vite how to transform JSX and what enables hot component reloading. If you need a path alias or a proxy to an API later on, this is where you configure it.

How the pieces fit together

flowchart TD
    A[Browser requests index.html] --> B["index.html contains div#root<br/>and loads /src/main.jsx"]
    B --> C[main.jsx: createRoot + render]
    C --> D[App.jsx: root component]
    D --> E[CicloUrbano components<br/>src/components/]
    F[Vite: development server] -.transforms JSX on the fly.-> B

  1. The npm scripts: dev, build, preview

Open package.json yourself:

{
  "name": "ciclourbano",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^19.1.0",
    "react-dom": "^19.1.0"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.4.0",
    "eslint": "^9.25.0",
    "vite": "^7.0.0"
  }
}
Script Command What it does When you use it
dev npm run dev Starts the development server with hot reload at localhost:5173 The whole time you're coding
build npm run build Generates the optimized production build in dist/ Before deploying
preview npm run preview Serves what's in dist/ locally so you can check it After build, to validate before deploying
lint npm run lint Scans the code for errors and bad practices Before committing changes to git

Try the full cycle now:

npm run build
vite v7.0.0 building for production...
✓ 34 modules transformed.
dist/index.html                   0.46 kB │ gzip:  0.30 kB
dist/assets/index-DiwrgTda.css    1.39 kB │ gzip:  0.72 kB
dist/assets/index-C9n2Xk4p.js   143.41 kB │ gzip: 46.12 kB
✓ built in 612 ms
npm run preview
  ➜  Local:   http://localhost:4173/

Notice two important details: preview uses a different port (4173) so you don't confuse it with the dev server, and the files in dist/ carry a hash in their name (index-C9n2Xk4p.js). That hash changes whenever the content changes, which lets the browser cache aggressively without ever serving a stale version.

On dependencies versus devDependencies: the former end up in the bundle that reaches the browser (react, react-dom); the latter are only used on your machine to build and analyze the code (vite, eslint). That's why the build output is far smaller than node_modules/.

  1. Hot Module Replacement

HMR (Hot Module Replacement) is the reason development with Vite feels so smooth. When you save a file, Vite doesn't reload the whole page — it sends only the changed module over a WebSocket, and React swaps it in live.

The practical difference is huge:

Full reload HMR
Time until you see the change 1-3 seconds Tens of milliseconds
Application state Lost — you're back to the start Preserved
Scroll and focus Reset Kept

The "state is preserved" part is what you'll appreciate the most: if you've half-filled a CicloUrbano booking form and you tweak a color in the CSS, you'll still see the form filled in.

Try it. With npm run dev running, open src/App.jsx, change any visible text, and save. The browser updates on its own, with no flicker. In the Vite console you'll see:

9:41:23 [vite] hmr update /src/App.jsx

There are cases where HMR can't apply the change and reloads the whole page instead: editing vite.config.js, touching index.html, or restructuring a module in a way Vite can't reconcile. That's normal.

  1. React DevTools in the browser

React DevTools is the official extension that lets you inspect your application in React's own terms — components, props, state — instead of in terms of HTML nodes.

Installation

  • Chrome / Edge: search for "React Developer Tools" in the Chrome Web Store and install it.
  • Firefox: search for it on addons.mozilla.org.
  • Safari or others: you can use the standalone version with npx react-devtools.

After installing it, open http://localhost:5173, press F12, and you'll see two new tabs: Components and Profiler.

What the Components tab shows

  • The component tree with real component names (App, BikeList, BikeCard), instead of a tangle of divs. This is the main reason to install it.
  • Props and state of the selected component, in the right-hand panel, editable on the fly so you can test cases without touching the code.
  • The parent component that renders it, and the full path up to the root.
  • A selector (the arrow icon) to click an element on the page and jump straight to its component.

Check that it works: open Components and you'll see App in the tree, with StrictMode inside it. There's not much to see yet, but from lesson 01-03 onward this tab will be your main diagnostic tool.

The Profiler tab measures render performance. It's an excellent tool, but it requires understanding how React renders first; it's covered in the lesson Measuring Performance with React DevTools Profiler.

  1. Why Vite and not Create React App

For years, the standard way to create a React project was create-react-app (CRA). Today you shouldn't use it:

  • It's discontinued. The official React documentation dropped it from its recommendations in 2023, and the package stopped being actively maintained. Installing it today shows warnings about outdated dependencies.
  • It's slow. CRA uses webpack with Babel and bundles the entire application before it can serve anything: starting a medium-sized project could take 30 seconds or more, and every change took several seconds. Vite serves ES modules natively and uses esbuild (written in Go) for dependencies: it starts in under a second regardless of project size.
  • It's opaque. Its configuration is hidden behind react-scripts, and customizing it meant running eject (an irreversible operation that dumps hundreds of lines of configuration into your repository) or resorting to external patches. vite.config.js is five readable lines.
Create React App Vite
Status Discontinued Active, de facto standard
Server startup Tens of seconds Under a second
Update after a change Seconds Milliseconds
Configuration Hidden; irreversible eject Short, editable file
Production build webpack Rollup, with code splitting included

Seeing CRA in an old tutorial is the best sign not to trust how current it is: if a tutorial uses create-react-app, it probably also uses class components and pre-hooks APIs. Treat it as historical context only.

Common Mistakes and Tips

  • Running npm run dev outside the project folder. This gives npm error Missing script: "dev". Check with pwd (or ls package.json) that you're inside ciclourbano/.
  • Forgetting the -- in the creation command. npm create vite@latest ciclourbano --template react doesn't pass the template to the wizard, so it'll ask you interactively instead. It's not a serious error, but it's worth knowing why it happens.
  • Committing node_modules/ to git. It's tens of thousands of files. The .gitignore Vite generates already excludes it — don't delete that line. If someone clones the repository, npm install rebuilds it.
  • Editing files inside node_modules/. Any change there disappears on the next install. If you need to modify a dependency, there are dedicated tools for it (patch-package), but that's almost never the right solution.
  • Running too old a version of Node. This is the number-one cause of baffling install or startup errors. Always check node --version before asking for help.
  • Closing the terminal running npm run dev and expecting the site to keep working. The development server is that process; kill it, and localhost:5173 stops responding.
  • Mixing up the dev port and the preview port. 5173 serves your code in development, 4173 serves the contents of dist/. If you edit something and don't see the change, check which port you're on.
  • Tip: add the project to git from day one (git init, git add ., git commit -m "Initial CicloUrbano project"). Being able to roll back to a working point is priceless while you're learning.

Exercises

Exercise 1

Create the CicloUrbano project following the steps in this lesson, and then:

  1. Change the <title> in index.html to CicloUrbano — Urban Bike Rental and the lang attribute to en.
  2. With the development server running, change any text in src/App.jsx and confirm the change appears without reloading the page.
  3. Run the production build and serve it locally. Note which port it uses and the size of the generated JavaScript file.

Exercise 2

Classify each of these files or folders into one of three categories: (A) I edit it regularly, (B) it exists but I almost never touch it, (C) I never edit it by hand.

src/App.jsx · node_modules/ · package.json · package-lock.json · index.html · vite.config.js · src/components/ · dist/

Exercise 3

Place two images in the project: logo-ciclourbano.svg in public/ and urban-bike.svg in src/assets/. Explain what path you'd use to reference each one, and what happens to each file when you run npm run build.

Solutions

Solution 1.

npm create vite@latest ciclourbano -- --template react
cd ciclourbano
npm install
npm run dev

Edit index.html:

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>CicloUrbano — Urban Bike Rental</title>
  </head>

When you save src/App.jsx with different text, the browser updates within milliseconds without losing state (if you'd clicked the sample counter, it keeps its value) — that's HMR in action. The Vite console prints [vite] hmr update /src/App.jsx.

For the third point:

npm run build
npm run preview

preview serves at http://localhost:4173/. The JavaScript file will be around 140-150 kB (about 45 kB gzip-compressed), which is essentially the weight of React and React DOM.

Solution 2.

File / folder Category Reason
src/App.jsx A It's your root component; you touch it constantly
src/components/ A This is where all the CicloUrbano components will live
package.json B Changes when you add scripts; dependencies are managed by npm install
index.html B Title, language, favicon and metadata; barely touched afterward
vite.config.js B Only when you need an alias, a proxy, or a plugin
node_modules/ C Generated by npm; any change is lost
package-lock.json C Managed by npm; committed to git but never edited by hand
dist/ C Output of npm run build; regenerated, and not even committed to git

Solution 3.

  • public/logo-ciclourbano.svg is served as-is from the site root. You reference it with an absolute path, and its name never changes:

    <img src="/logo-ciclourbano.svg" alt="CicloUrbano" />
    

    After npm run build it shows up at dist/logo-ciclourbano.svg with the same name. It's the right choice when the path needs to be stable and predictable (favicon, robots.txt, images referenced from outside).

  • src/assets/urban-bike.svg is imported from the code:

    import urbanBike from './assets/urban-bike.svg';
    
    <img src={urbanBike} alt="Urban bike" />
    

    When it builds, Vite processes it, renames it with a hash (dist/assets/urban-bike-B7kX2p1q.svg), and swaps in the reference. Advantages: optimal browser caching, a build-time error if the path is misspelled, and automatic removal if the file stops being used.

Conclusion

You now have a professional development environment: Node.js LTS and npm verified, an editor with ESLint and Prettier, the CicloUrbano project created with Vite, the development server running with hot reload, and React DevTools installed in your browser. You also know what every generated file does, how src/assets/ differs from public/, what each npm script is for, and why Vite has replaced Create React App.

So far you've only run someone else's code. In the next lesson, Hello World in React, you'll clean up the sample template, understand line by line how main.jsx mounts the application onto the DOM, and write your first two components of your own: Welcome and BikeCard.

React Course

Module 1: Getting Started with React

Module 2: React Components

Module 3: Working with Events

Module 4: Advanced Component Concepts

Module 5: React Hooks

Module 6: Routing in React

Module 7: State Management

Module 8: Performance Optimization

Module 9: Testing React Applications

Module 10: Advanced Topics

Module 11: Project: Building a Complete Application

© Copyright 2026. All rights reserved