How to Create a React App with Vite
Create a modern React app with Vite, understand the generated project, build a real component, use environment variables safely, and produce a deployment-ready build.

Install a Node.js version supported by the current Vite release, then run npm create vite@latest my-react-app -- --template react. Enter the project, install dependencies with npm install, and start development with npm run dev. Use the react-ts template instead when you want TypeScript. Before deployment, run npm run build and inspect the generated dist folder with npm run preview.
Before you create the app
Vite is a frontend build tool with a fast development server and an optimized production build. The React template configures JSX, React Fast Refresh, linting, and the scripts needed for a client-side application. It deliberately does not choose routing, data fetching, authentication, testing, or a backend for you.
You need
- A supported Node.js release and npm. The current Vite documentation requires Node.js 20.19+ or 22.12+; a template may require a newer release.
- A terminal opened in the parent folder where the project should be created.
- A code editor such as VS Code or another editor with JavaScript and JSX support.
- A modern browser for Vite's development server.
- Git if you plan to track the project and publish it to a remote repository.
node --version
npm --version
git --versionMissing a prerequisite?
Decide whether Vite is the right React starting point
Vite is a strong choice for learning React, browser-only dashboards, embedded interfaces, prototypes, and client-rendered single-page applications. It gives you a clean build foundation without hiding the application structure.
| Project | Start with | Why |
|---|---|---|
| Learning React or a client-only SPA | Vite + React | Small setup, fast feedback, and direct control over routing and data choices. |
| Marketing site that needs strong per-page SEO | A React framework or static-site framework | Server rendering or pre-rendering is usually part of the architecture, not an afterthought. |
| Full-stack React application | A recommended full-stack React framework | Routing, server code, data loading, forms, and deployment conventions are integrated. |
| React inside an existing server-rendered site | Vite integration or incremental React | Add isolated interactive roots without rebuilding the entire application. |
Create the Vite and React project
The create-vite initializer copies a maintained template into a new folder. With npm 7 and newer, the extra double dash passes the template option through npm to create-vite.
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev| Package manager | React JavaScript template |
|---|---|
| npm | npm create vite@latest my-react-app -- --template react |
| pnpm | pnpm create vite my-react-app --template react |
| Yarn | yarn create vite my-react-app --template react |
| Bun | bun create vite my-react-app --template react |

Open and verify the development app
- 01
Read the terminal URL
npm run dev starts Vite's development server and prints its local address. Open the exact URL shown rather than assuming the port is always the same.
- 02
Make one visible edit
Open src/App.jsx or src/App.tsx, change the heading, and save. The browser should update through React Fast Refresh without a full reload.
- 03
Check the browser console
Open developer tools and resolve red errors or failed network requests. A page that looks correct can still have runtime problems.
- 04
Stop the server deliberately
Return to the terminal and press Ctrl+C. The development server is a running process; closing a browser tab does not stop it.
# Start normally
npm run dev
# Expose to other devices on your local network
npm run dev -- --host
# Request a specific port and fail if it is occupied
npm run dev -- --port 5173 --strictPortUnderstand the generated project structure
| Path | Purpose | First-day guidance |
|---|---|---|
| index.html | The HTML entry document Vite transforms and serves. | Keep the root element that React mounts into; update metadata as the app becomes real. |
| src/main.jsx | Creates the React root and renders the top-level component. | Providers, global CSS, and app-wide setup often begin here. |
| src/App.jsx | The starter application component. | Replace the demo, then split features into focused components. |
| src/assets/ | Source assets imported by application code. | Imported files are processed, hashed, and included in the build graph. |
| public/ | Files copied to the build root without transformation. | Reference them from root paths and use this folder only when transformation is unwanted. |
| vite.config.js | Vite plugins and build or server configuration. | Keep changes minimal; defaults are enough for the first app. |
| eslint.config.js | Lint rules for the generated source. | Run the lint script before committing. |
| package.json | Dependencies and project scripts. | Commit it together with package-lock.json. |
src/
assets/
components/
ProjectCard.jsx
data/
projects.js
App.jsx
App.css
index.css
main.jsxReplace the starter with a useful React component
The example below proves the essential React loop: component state changes in response to an event, React renders the new value, and Vite updates the module quickly while you work.
import { useState } from 'react'
import './App.css'
export default function App() {
const [tasks, setTasks] = useState(0)
return (
<main className="app-shell">
<p className="eyebrow">VITE + REACT</p>
<h1>Your project is ready.</h1>
<p>You have completed {tasks} setup {tasks === 1 ? 'task' : 'tasks'}.</p>
<button type="button" onClick={() => setTasks((count) => count + 1)}>
Complete a task
</button>
</main>
)
}.app-shell {
width: min(680px, calc(100% - 2rem));
margin: 10vh auto;
padding: clamp(2rem, 6vw, 4rem);
border: 1px solid #c9c7df;
border-radius: 1.25rem;
background: #ffffff;
box-shadow: 0 24px 70px rgba(37, 32, 73, 0.12);
}
.eyebrow {
color: #6656d9;
font-weight: 800;
letter-spacing: 0.12em;
}
button {
padding: 0.8rem 1rem;
border: 0;
border-radius: 0.65rem;
color: white;
background: #5b48d6;
cursor: pointer;
}Know what the Vite scripts actually do
| Command | Purpose | Important limit |
|---|---|---|
| npm run dev | Starts the development server with module updates and React Fast Refresh. | Development only; it is not the production server. |
| npm run build | Creates optimized static production assets in dist. | A successful build does not prove routing or runtime API behavior on the host. |
| npm run lint | Runs the generated ESLint configuration against source files. | Lint catches defined rule violations, not every application bug. |
| npm run preview | Serves the already-built dist directory for a local production check. | It is a preview tool, not a production server. |
Hot Module Replacement updates changed modules while preserving as much application state as possible. React Fast Refresh builds on that behavior for components. When a change cannot be refreshed safely, a full reload is expected.
Install and remove project dependencies
Install libraries inside the project folder so they are declared in package.json. Application code should be able to recreate its dependency tree from the committed manifest and lockfile.
# Runtime dependency used by application code
npm install date-fns
# Development-only tool
npm install --save-dev vitest
# Remove a package cleanly
npm uninstall date-fns
# Inspect direct dependencies
npm list --depth=0Before adding a package
- Confirm the package solves a real requirement that is not simpler to implement directly.
- Check its documentation, maintenance activity, license, and supported React version.
- Review the package name carefully to avoid a lookalike dependency.
- Understand whether it adds browser bundle weight or requires server-only APIs.
- Commit package.json and package-lock.json together after the change.
Use environment variables without leaking secrets
Vite exposes built-in mode values through import.meta.env. Custom variables must use the VITE_ prefix to become available in client code. That prefix means public-to-the-browser, not secure.
VITE_API_BASE_URL=https://api.example.test
# Never put a private database password or server secret here.const apiBaseUrl = import.meta.env.VITE_API_BASE_URL
if (import.meta.env.DEV) {
console.info('Using API:', apiBaseUrl)
}| File | Loaded when | Commit? |
|---|---|---|
| .env | Every mode. | Only when it contains non-secret shared defaults. |
| .env.local | Every mode, with higher priority than .env. | No; keep *.local ignored. |
| .env.development | Development mode. | Only for safe shared development values. |
| .env.production | Production mode during the normal build. | Only for safe shared production values. |
| .env.[mode].local | One mode with local priority. | No. |
Handle images, public files, and base paths correctly
import dashboardImage from './assets/dashboard.png'
export function Hero() {
return <img src={dashboardImage} alt="Project dashboard" />
}Imported assets participate in the module graph. Vite can process them, give production files cache-friendly names, and report a missing file during the build.
Build, preview, and deploy the application
- 01
Create the production bundle
Run npm run build. Vite uses index.html as the default entry and writes optimized assets to dist unless the configuration changes the output directory.
- 02
Preview the exact build
Run npm run preview and open the printed URL. Test navigation, refreshing a nested route, API calls, responsive layouts, and browser-console errors.
- 03
Deploy the dist output
Configure the hosting platform to run npm ci and npm run build, then publish dist. Do not upload src as the finished site.
- 04
Configure SPA fallback when routing
A client-side router needs the host to return index.html for application routes. Without a fallback, refreshing /settings can produce a host-level 404 even though in-app navigation works.
npm run lint
npm run build
npm run preview| Setting | Value | Why |
|---|---|---|
| Install command | npm ci | Uses the committed package-lock.json exactly. |
| Build command | npm run build | Creates the optimized site. |
| Publish directory | dist | This is Vite's default build output. |
| Environment variables | Set in the host dashboard | Production values are injected when the host builds the app. |
| SPA rewrite | All app routes to /index.html | Allows the client router to handle direct visits and refreshes. |
Put the Vite app under version control
The template includes a useful .gitignore. Review it before the first commit, especially after adding local environment files or tool-specific output.
git init
git add .
git status
git commit -m "Create React app with Vite"
# After creating an empty GitHub repository
git remote add origin YOUR_REPOSITORY_URL
git push -u origin mainCommit these, not generated local state
- Commit source files, public assets, configuration, package.json, and package-lock.json.
- Do not commit node_modules; npm recreates it from the manifest and lockfile.
- Do not commit .env.local or files containing credentials.
- Commit intentional configuration changes instead of undocumented editor-only workarounds.
- Run lint and build before pushing a branch for review.
Fix common React and Vite setup problems
| Problem | Likely cause | Fix |
|---|---|---|
| Unsupported engine or Node version | The installed Node.js release is below Vite or template requirements. | Check node --version and install a supported LTS patch; do not use --force to hide the requirement. |
| npm create vite fails | Registry access, proxy configuration, or an outdated Node/npm environment. | Check npm config get registry, network policy, and the first error line; retry after fixing the cause. |
| Port already in use | Another development server owns the preferred port. | Use the URL Vite selects, stop the other process, or pass --port with --strictPort. |
| Blank page | A runtime exception, wrong root element, or broken import prevents rendering. | Open the browser console and terminal, then fix the first error rather than changing random files. |
| Module not found | The package is not installed, the path or letter case is wrong, or the terminal is in another folder. | Confirm pwd, inspect package.json, and match the actual filename exactly. |
| Changes do not appear | The file is not imported, the server is stale, or the browser is showing another port. | Check the terminal URL and import path; restart the server when config or env files changed. |
| Environment variable is undefined | It lacks the VITE_ prefix or the server has not restarted. | Rename only client-safe values with VITE_, restart Vite, and read import.meta.env rather than process.env. |
| Nested route returns 404 after deployment | The static host lacks SPA fallback or the base path is wrong. | Configure the host rewrite and align Vite base plus router settings with the deployed path. |
node --version
npm --version
npm list vite react react-dom
npm run lint
npm run buildChoose the next capability deliberately
| Need | Add deliberately | Question to answer first |
|---|---|---|
| Multiple screens | A maintained router | Will this remain a client SPA, or does the app need framework-level data and rendering? |
| Remote data | A small API client and explicit loading, error, empty, and success states | Where are credentials kept, and what is the API's browser security model? |
| Component tests | A test runner and DOM testing utilities | Which user behavior must remain stable? |
| Formatting | A formatter integrated with the existing lint workflow | Can the team run one documented command locally and in CI? |
| Server rendering or backend routes | A React framework or documented backend integration | Has the project outgrown a client-only static build? |
Primary learning references
Frequently asked questions
What command creates a React app with Vite?
With npm, run npm create vite@latest my-react-app -- --template react. Then enter the folder, run npm install, and start the development server with npm run dev.
Should I choose the react or react-ts template?
Choose react for JavaScript or react-ts when you want TypeScript from the beginning. Both use the same Vite scripts and React development workflow.
Why is there an extra double dash in the npm command?
npm uses -- to pass the following options to create-vite. Without it, --template may be interpreted by npm instead of the initializer.
Is Vite a React framework?
No. Vite is a build tool and development server. Its React template configures a client application, but you choose routing, data loading, authentication, backend behavior, testing, and deployment architecture.
Should I still use Create React App?
No for a new project. Create React App is deprecated. Use a recommended React framework for integrated application architecture, or a build tool such as Vite when a client-side app or from-scratch setup is appropriate.
Which Node.js version does Vite require?
The current Vite documentation requires Node.js 20.19+ or 22.12+, and a template can require a newer release. Check the official Vite guide when installing because requirements change across major releases.
What is the difference between npm run dev and npm run preview?
npm run dev serves source modules with development features such as Fast Refresh. npm run preview serves the already-generated dist build for local inspection. Neither command is intended to be the production server.
Where does Vite put the production build?
By default, npm run build writes the optimized static output to dist. Configure your static host to publish that folder unless vite.config changes build.outDir.
Are VITE_ environment variables secret?
No. Values prefixed with VITE_ are exposed to client code and included in the browser bundle. Keep sensitive credentials on a backend or in serverless functions.
Why does refreshing a React route return 404 after deployment?
The host is looking for a physical file at that path. Configure a single-page-application rewrite to index.html and ensure the Vite base and client router match the deployment path.
Can I deploy a Vite React app to static hosting?
Yes. Build the app and deploy the dist directory. Configure production environment values, the correct base path, and an SPA fallback when client-side routes are used.