Get Started showed you the one command that creates a React project. This lesson is about what that command produced — every generated file, what it is for, and the two or three details that are worth understanding before you start changing them.
Vite is what the React documentation points you at now that create-react-app is
retired. It is the smallest thing that gets you a real React project, and it is what the demo
application this track draws from is built with.
The version this is written against
Vite 8 and @vitejs/plugin-react 6, on Node 22. Vite 8 requires Node 20.19 or
newer — check that first, because the error it gives on an older Node is not obviously
about Node:
node --version
# v22.23.2Create the project
To recap:
npm create vite@latest pizza-react-frontend -- --template react-ts
cd pizza-react-frontend
npm install
npm run devThat is the whole setup. npm create vite@latest downloads the scaffolder and throws
it away afterwards, so there is nothing installed globally to go stale.
The -- matters. Without it npm swallows the flags and runs the interactive prompts
instead, which is fine but slower. react-ts is the TypeScript template; there is a
plain react template too, and this track uses the TypeScript one throughout.
The dev server comes up on http://localhost:5173 in well under a second and reloads
the moment you save. That speed is the reason Vite won: it does not bundle your source in
development at all, it serves the modules straight to the browser.
What the scaffolder gave you
pizza-react-frontend/
├── index.html ← the real entry point (see below)
├── package.json
├── tsconfig.json ← project references, splitting app from build tooling
├── tsconfig.app.json ← how YOUR source is typechecked
├── tsconfig.node.json ← how vite.config.ts is typechecked
├── vite.config.ts
├── public/ ← copied to the output untouched, never processed
│ └── favicon.svg
└── src/
├── main.tsx ← where React attaches itself to the page
├── App.tsx ← the root component
└── assets/Two of those are worth stopping on.
index.html is the entry point, and it is at the project root, not in
public/. That surprises people coming from webpack, where the HTML file is a
template a plugin fills in. In Vite the HTML file is the source of truth and it is where the
application starts — the <script type="module"> at the bottom is a real module
import that Vite follows:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PizzaHub — order pizza online</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>One empty <div>. Everything a visitor sees is built by React and put inside it
— that is what a single-page application means, and it is the subject of
Rendering to the DOM.
vite.config.ts starts out almost empty, and in this project it never
grew:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})The React plugin is what compiles JSX and wires up Fast Refresh (component state survives a save, so you do not lose the form you were filling in). If you find yourself adding a lot here, check first whether Vite already does it — it handles TypeScript, CSS, Sass, JSON imports, static assets and environment variables with no configuration at all.
The scripts you will actually run
Here is the pizza app's package.json, which started as the template's and grew:
{
"name": "pizza-react-frontend",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview",
"typecheck": "tsc -b --noEmit",
"lint": "oxlint",
"test:e2e": "playwright test"
},
"dependencies": {
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-router-dom": "^7.18.2",
"react-bootstrap": "^2.10.10",
"bootstrap": "^5.3.8"
},
"devDependencies": {
"@vitejs/plugin-react": "^6.0.4",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"typescript": "~6.0.2",
"vite": "^8.2.0"
}
}npm run dev— the one you live in.npm run build— note it istsc -b && vite build. Vite alone strips types without checking them, which is what makes it fast; runningtscfirst is what stops a type error reaching production. Do not remove it.npm run preview— serves the built output. The dev server and the real build differ in ways that occasionally matter, so check here before deploying.
react and react-dom are two packages on purpose: react is
the component model and knows nothing about browsers, react-dom is the renderer that
turns it into DOM nodes. React Native swaps the second one out and keeps the first.
TypeScript, briefly
The template splits tsconfig.json into two referenced projects — one for your source,
one for the build tooling — because they run in different environments.
vite.config.ts runs in Node and may import path; src/ runs in
a browser and may touch document. Keeping them separate means each gets the right
global types and neither can accidentally use the other's.
You do not have to write types everywhere. TypeScript infers most of them, and this track only adds annotations where they earn their place — component props, mostly.
Next
Before the first component, a short detour through the JavaScript that React code leans on hardest: The JavaScript You Need First.