Module 1: Getting Started
Setting Up a React Project with Vite
Install Node, scaffold a Vite project, understand every file it gives you, and learn how the dev server actually works.
You can learn React in a browser sandbox, and that's fine for tiny experiments. But real work happens in a local project, with a dev server, a build step and a package manager. Getting comfortable with that setup early saves you from a lot of confusion later.
Check your Node version#
React itself runs in the browser. The tools that bundle and serve your code run on Node.js, so you need it installed.
node -v
npm -v
The current Vite release needs Node 20.19 or newer, or 22.12 and up if you're on the 22 line. If your version is older, Vite will print a warning or refuse to start. Installing the current LTS version from nodejs.org is the simplest fix. If you switch between projects often, use a version manager such as nvm or fnm.
Why Vite and not Create React App#
Create React App is deprecated. It's slow, no longer maintained, and the React docs have stopped recommending it. You may still meet it in old tutorials and old codebases. Don't start new projects with it.
Vite is the standard choice for a client-side React app. If you later need server rendering or file-based routing out of the box, frameworks like Next.js or React Router's framework mode are the next step, but you don't need them to learn React.
Create the project#
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run dev
That double dash matters. Everything after -- is passed to the Vite generator instead of being swallowed by npm. If you forget it, newer versions of the tool just ask you questions interactively, which is also fine.
The dev server prints an address, usually http://localhost:5173. Open it and you should see the starter page with a counter button.
If you want TypeScript later, use --template react-ts. This course uses plain JavaScript so there's less syntax between you and the ideas.
What you just got#
my-app/
├── index.html the only HTML page in the whole app
├── package.json dependencies and scripts
├── vite.config.js build and dev server settings
├── eslint.config.js lint rules
├── public/ files copied as-is (favicon, robots.txt)
└── src/
├── main.jsx entry point, mounts React into the page
├── App.jsx your root component
├── App.css
├── index.css
└── assets/ images and such that you import from code
Open index.html. The important part is tiny:
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
That empty div is where your whole application will live. The script tag loads main.jsx, which is the real starting point.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
createRoot(document.getElementById("root")).render(
<StrictMode>
<App />
</StrictMode>
);
Read that slowly. createRoot tells React "this DOM node is yours." render hands it a tree of components. After that, React owns everything inside that div, and you should not touch it with document.querySelector and friends.
How the whole thing starts up#
browser loads index.html
│
▼
<script type="module" src="/src/main.jsx">
│
▼
main.jsx ──► createRoot(#root).render(<App />)
│
▼
App ──► child components ──► DOM nodes
Why the dev server feels instant#
During development, Vite does not bundle your whole app before serving it. It leans on the browser's native ES module support. The browser requests main.jsx, Vite transforms that one file (turning JSX into plain JavaScript) and sends it back. The browser sees the imports inside it, requests those files, and so on.
When you save a file, Vite only re-sends the file that changed, and a mechanism called HMR (hot module replacement) swaps the new code into the running page. For React components, the React plugin keeps component state when it can, so you can tweak styling while staying in the same form step.
For production, npm run build does a proper bundle: minified, split into chunks, with hashed filenames for caching. We'll use that in the deployment lesson.
StrictMode, briefly#
StrictMode is a development-only wrapper. It renders your components twice and re-runs effects once extra, specifically to flush out code that isn't pure or doesn't clean up after itself. It does nothing in production builds.
If you ever see a console.log print twice and think React is broken, this is why. Leave StrictMode on. It finds real bugs.
Environment variables#
Vite exposes variables that start with VITE_ to your client code:
# .env
VITE_API_URL=https://api.example.com
const apiUrl = import.meta.env.VITE_API_URL;
Anything with that prefix is copied into the JavaScript bundle and is visible to every visitor. Don't put a secret key in there. If a value would hurt you in someone else's hands, it belongs on a server.
A folder layout that scales a bit#
For small apps the default is fine. Once you have more than a dozen components, I group by feature:
src/
├── components/ shared, generic pieces (Button, Modal)
├── features/
│ ├── cart/ everything about the cart
│ └── auth/
├── hooks/ shared custom hooks
├── pages/ route-level components
└── lib/ helpers, API clients
Don't agonize over it now. Moving files is cheap.
Where people get burned#
- Running
npm run devfrom the wrong folder and getting "missing script". Check you're in the folder withpackage.json. - Forgetting
npm installafter cloning a project. - Committing
node_modules. Make sure it's in.gitignore(Vite's template already does this). - Naming a file
app.jsxand importing./App.jsx. File systems on Linux are case sensitive, so this works on your Mac and breaks in deployment. - Putting secrets in
VITE_variables.
Practice#
- Create a project, start the dev server, and change the text in
App.jsx. Watch the page update without a reload. - Delete everything inside
App.jsxand replace it with a component that returns a singleh1with your name. - Add
console.log("render")at the top ofAppand note how many times it prints on load. Then removeStrictModetemporarily and compare. Put it back afterwards. - Add a
.envfile with aVITE_GREETINGvalue and display it in the page. - Run
npm run buildand look at what lands indist/.