Key Takeaways
- Start with a maintained React framework for an application with integrated routing and rendering needs; use Vite for a straightforward client-rendered project.
- Node.js runs the development tools shown here. The resulting client-side React application runs in the browser.
- Babel transforms JSX and JavaScript syntax according to your browser targets; it isn't mandatory for every React setup.
- The custom Webpack route below includes dependency installation, Babel configuration, HTML handling, component rendering, local development, and a production build.
For most React setup work, choose a maintained framework or start a client-rendered project with Vite rather than assembling a build pipeline yourself. React recommends frameworks for new applications, while its from-scratch guidance supports using a build tool when you're learning or have specific constraints. This tutorial covers the quick Vite route, then builds a complete Webpack and Babel project so you understand the moving parts.
If components and rendering are new to you, read the React introduction alongside this setup guide.
Choose Your React Setup
| Option | Choose it when | Your responsibility |
|---|---|---|
| React framework, such as Next.js or React Router framework mode | You want integrated routing, data loading, or server-rendering options. | Learn the framework's application and deployment conventions. |
| Vite | You want a small client-rendered app or a focused environment for learning React. | Add application-level choices, such as routing, when needed. |
| Custom Webpack and Babel | You need to understand or control this particular build pipeline. | Maintain the configuration and integrations yourself. |
A framework doesn't automatically mean you must operate a server: React's recommended web frameworks also support client rendering and static deployment. Use the official framework overview to compare their capabilities.
What Node.js, Babel, and Webpack Actually Do
Node.js Runs Your Development Tools
Node.js executes JavaScript outside the browser. Here, you use it to run package installation, development servers, and production builds—not to execute your UI for visitors. A static deployment of the client-only application doesn't need a Node.js application server.
Babel Transforms JSX and Unsupported Syntax
JSX is a syntax extension that lets you describe UI using markup-like expressions inside JavaScript. Browsers don't directly execute JSX, so it must be transformed before execution. Babel is one compiler that can do that; frameworks and build tools can arrange the transformation without a handwritten Babel configuration.
Babel's JavaScript output depends on its configured targets. It doesn't universally turn everything into ES5 or guarantee support for every browser. Transforming syntax also doesn't automatically supply missing browser APIs.
React can run without a custom build configuration, and JSX itself is optional. A browser-only approach still needs browser-resolvable module imports and either transformed JSX or code that doesn't use JSX. See the React JSX tutorial for the syntax distinction.
Webpack Bundles Modules; Its Dev Server Serves Them
Webpack follows your imports and produces bundles. In the custom setup, babel-loader connects Babel to Webpack. Local serving comes from the separately installed webpack-dev-server package, not from Webpack's production bundler alone.
Node.js runs the build tools
Webpack follows imports from src/index.js
|-- babel-loader -> Babel transforms JSX and syntax
|-- public/index.html -> copied HTML
|
|-- development -> webpack-dev-server -> browser
`-- production -> dist/ -> static hosting -> browser
The browser executes the client-side React application.Install Node.js for Your React Environment
Install a current patched release of Node.js 24 LTS from the official Node.js download page. The custom Babel 8 setup requires at least Node.js 24.11.0 within that release line. This is the tutorial's compatibility baseline, not a claim that the examples were executed in this editorial environment.
Open a new terminal and check that both commands resolve:
node --version
npm --versionQuick React Setup with Vite
For the lightweight route, use Vite's React JavaScript template. If the scaffolder offers to install dependencies and start immediately, decline so you can follow the remaining commands yourself.
npm create vite@latest hello-react-vite -- --template react
cd hello-react-vite
npm install
npm run devOpen the local URL printed in the terminal. The template supplies the React build integration; you don't need the custom Webpack files below. Our Vite-based React quick start continues this route.
Stop the development server with Ctrl+C, then build and inspect the production output:
npm run build
npm run previewVite writes production output to dist by default. Its preview server is for local inspection, not production hosting. The scaffolding command deliberately uses the current generator; commit the generated package.json and lockfile to preserve your project's dependency resolution.
Complete React Webpack Setup
Choose this route to learn or control Webpack and Babel. It uses React 19, Babel 8, Webpack CLI 7, webpack-dev-server 6, and babel-loader 10. Webpack itself is pinned to 5.112.0 because the HTML-copying configuration below requires a recent Webpack release.
1. Create a Separate Project
Start in the parent directory where you keep your projects—not inside the Vite application.
mkdir hello-react-webpack
cd hello-react-webpack
npm init -y
mkdir src publicnpm init -y creates a package.json without the interactive questionnaire. Your completed project will have this layout:
hello-react-webpack/
|-- package.json
|-- package-lock.json
|-- babel.config.json
|-- webpack.config.cjs
|-- public/
| `-- index.html
|-- src/
| |-- App.js
| `-- index.js
`-- dist/ (generated by the production build)2. Define Scripts and Browser Targets
Before installing dependencies, replace the generated package.json with this complete starter file. The installation commands in the next step will add its dependency sections.
{
"name": "hello-react-webpack",
"version": "1.0.0",
"private": true,
"type": "module",
"engines": {
"node": ">=24.11.0 <25"
},
"browserslist": [
"defaults"
],
"scripts": {
"start": "webpack serve --config webpack.config.cjs --mode development",
"build": "webpack --config webpack.config.cjs --mode production"
}
}Browser targets are the browsers you intend to support. This project explicitly selects Browserslist's defaults query, which Babel can use for source transformations and Webpack can use for its generated runtime. Replace that query when your application has a different support policy.
3. Install React and the Build Dependencies
npm install --save-exact react@19 react-dom@19
npm install --save-dev --save-exact webpack@5.112.0 webpack-cli@7 webpack-dev-server@6
npm install --save-dev --save-exact @babel/core@8 @babel/preset-env@8 @babel/preset-react@8 babel-loader@10react and react-dom are application dependencies. Webpack, its CLI and development server, Babel, and the loader are development/build dependencies, recorded under devDependencies by --save-dev. They must be available wherever you build the application.
The major selectors resolve package versions within those families; --save-exact records the resolved versions rather than version ranges. Commit both package.json and package-lock.json, and use npm ci for subsequent clean installations. Without sharing the lockfile, these commands aren't a frozen dependency snapshot.
4. Configure Babel to Compile React JSX
Create babel.config.json in the project root:
{
"presets": [
"@babel/preset-env",
[
"@babel/preset-react",
{
"runtime": "automatic"
}
]
]
}@babel/coresupplies the compiler.@babel/preset-envchooses JavaScript transformations using the browser-target configuration.@babel/preset-reacttransforms JSX.- The automatic JSX runtime inserts the required JSX helper imports, so components don't need
import Reactsolely to use JSX.
Installing presets isn't enough: this configuration is what enables them. The setup doesn't add browser-API polyfills.
5. Configure Webpack and Its Development Server
Create webpack.config.cjs:
const path = require('node:path');
module.exports = {
entry: './src/index.js',
target: 'browserslist',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/',
clean: true,
copy: 'public'
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
},
devServer: {
static: {
directory: path.resolve(__dirname, 'dist')
},
host: 'localhost',
port: 9000,
open: true,
hot: false,
liveReload: true
}
};The application source uses ES module imports. This configuration deliberately uses CommonJS—require and module.exports. The .cjs extension keeps it CommonJS even though package.json contains "type": "module".
output.copy, available since Webpack 5.111.0, copies the contents of public into the output directory. That makes the HTML part of the build instead of leaving it stranded in the project root. clean: true clears old output, so don't keep hand-authored source files in dist.
The server configuration uses static.directory, not the obsolete contentBase setting. open: true requests browser opening, and disabling hot module replacement lets this minimal setup use full-page live reload. It doesn't configure React Fast Refresh.
For more bundler background, continue with the Webpack quick-start tutorial.
6. Create the HTML Document
Create public/index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello React</title>
</head>
<body>
<div id="root"></div>
<script defer src="/bundle.js"></script>
</body>
</html>This example assumes deployment at the site's root. The root-relative /bundle.js reference matches the configured output filename and public path. If you deploy under a subdirectory, update both the HTML script URL and output.publicPath to match.
7. Create a React Component
Create src/App.js:
export default function App() {
return (
<main>
<h1>Hello React!</h1>
<p>Your development environment is ready.</p>
</main>
);
}8. Render the Component from the Entry File
Create src/index.js:
import { createRoot } from 'react-dom/client';
import App from './App.js';
const container = document.getElementById('root');
if (!container) {
throw new Error('Missing #root element in the HTML document.');
}
const root = createRoot(container);
root.render(<App />);The installed React 19 packages support createRoot from react-dom/client. It creates a React root inside the supplied DOM element; root.render displays your component. The explicit container check makes a missing or misspelled HTML ID easier to diagnose.
9. Run and Verify the Application
npm startWith port 9000 available, this configuration serves the app at http://localhost:9000 and requests that your browser open it. You should see “Hello React!” Change that greeting in App.js, save, and confirm the page reloads.
Use the development-server URL rather than opening the HTML file directly from disk. Check the browser console if the page is blank, and confirm that the bundle request succeeds. Development serving and writing a production build are separate operations.
10. Create a Production Build
Stop the development server and run:
npm run buildThe configured production mode enables production optimizations, including minification. This project writes dist/index.html and dist/bundle.js; additional generated files, such as license notices, may also be present. Deploy the contents of dist through static hosting rather than running the development server publicly.
For a clean build from a committed checkout, run:
npm ci
npm run buildnpm ci requires an existing lockfile and fails if its dependency declarations don't match package.json. Keep build dependencies installed until the build finishes. Add node_modules/ and dist/ to your repository's ignore rules.
Common Mistakes
- JSX parsing errors: Check that
babel.config.jsonexists and the loader rule matches your source files. Installing Babel packages alone doesn't enable the React preset. - A missing page or bundle: Keep the HTML in
public/index.htmlso the configured copy step includes it. Check the browser's network panel for failed requests. - Port conflicts: If port 9000 is occupied, stop the conflicting process or change
devServer.port. - Stale production JavaScript: This learning setup uses a fixed
bundle.jsfilename. Don't apply permanent immutable caching to it; use content-hashed assets and matching HTML generation when you need long-lived caching. - Secrets in frontend configuration: Don't put private credentials in browser code. In Vite, exposed
VITE_*environment-variable values are bundled into client-side source, not hidden from users.
FAQ
What is the recommended way to start a React project in 2026?
React recommends starting new applications with a maintained framework. For a straightforward client-rendered project or a learning exercise, Vite is a suitable lightweight alternative; configure Webpack yourself when learning or controlling that toolchain is the goal.
Do I need Node.js to use React?
Not to run client-side React in a browser. You need Node.js for the installation and build tools demonstrated here, but the resulting static application doesn't require a Node.js application server.
Do I need Babel to use JSX?
You need a tool that transforms JSX, but it doesn't have to be Babel. A framework or Vite-based setup handles that integration for you; the custom route here explicitly configures Babel's React preset.
Is Create React App still maintained?
Create React App was deprecated for new applications on February 14, 2025 and placed in maintenance mode. Start new projects with a maintained framework or a build tool such as Vite instead.
How do I create a production build of a React app?
Run npm run build in either project shown here. Vite uses dist as its default output directory, and the custom Webpack configuration also writes its deployable files to dist. Deploy those files through production hosting, not the development server.
Further Reading
- Creating a React App — React's framework recommendations and rendering options.
- Sunsetting Create React App — deprecation details and migration directions.
- Vite Getting Started — scaffolding, templates, and local development.
- Babel preset-env — browser targets and syntax transformations.
- Babel preset-react — JSX transformation and runtime options.
- Webpack Output Configuration — emitted assets, copying, cleaning, and public paths.
- Webpack Development Server Configuration — static directories, ports, and reload behavior.
- React createRoot Reference — mounting components and diagnosing container errors.
