Advertisement

Home/Coding & Tech Skills

How to Build a React App from Scratch in 2026: 7 Steps to Get It Live

coding-tech-skills · Coding & Tech Skills

Advertisement

I remember my first React app in 2018—I spent an entire afternoon just trying to get a "Hello World" to render because I'd followed a tutorial that used a tool already broken by the time I hit Enter. By 2026, that scenario feels almost quaint. If you type npx create-react-app my-app today, you'll get a deprecation warning and a suggestion to switch to something faster. The ecosystem has moved on, and so should you.

Advertisement

This guide isn't a rehash of documentation. It's the exact path I'd take if I were starting a brand-new React project tomorrow—no fluff, no outdated commands, just the steps that actually work in 2026. By the end, you'll have a live app you can show your friends, your boss, or that hiring manager who asked for "React experience." Let's cut the chatter and start building.

Why Build a React App from Scratch in 2026?

React is still the most-used front-end library on the web, but the landscape around it has changed. Frameworks like Next.js and Remix have eaten a big slice of the pie, especially for production-grade apps. So why would you bother building a plain React app from scratch in 2026?

Because fundamentals matter. When you scaffold a project with Vite (the modern standard), you own every file. You decide when to add routing, how to fetch data, and what state management to use. That control is invaluable when you later move to Next.js or another meta-framework—you'll understand exactly what the framework is doing for you. Plus, for smaller apps (a personal portfolio, a landing page with dynamic content, an internal tool at your company), a plain React app is often lighter and faster than a full framework. I've shipped two such apps this year alone, and they load faster than most sites I visit daily.

Another reason: learning React from scratch forces you to write clean component logic. You won't rely on file-based routing or server-side rendering crutches. You'll learn to think in components, hooks, and state. That mindset is transferable to any JavaScript UI library.

But here's my honest take: if you're building a content-heavy site or need SEO out of the box, reach for Next.js. For anything else—a dashboard, a tool, a prototype—plain React with Vite is still a fantastic choice. And if you're learning, starting with a bare-bones React app is the fastest way to internalize the core concepts. You can always graduate to a framework later.

<>

Step 1: Set Up Your Development Environment

Before you write a single line of React code, you need three things installed on your machine:

  1. Node.js (version 18 or higher) — Download from nodejs.org. I recommend the LTS version; it's stable and well-tested. To check your current version, open a terminal and run node -v.
  2. npm or yarn — npm comes bundled with Node.js. If you prefer yarn, install it globally with npm install -g yarn. I use npm in this guide because it's the default, but the commands are almost identical.
  3. A code editor — Visual Studio Code is the industry standard. Install it from code.visualstudio.com. Then add the ES7+ React/Redux/React-Native snippets extension for handy shortcuts like rafce (which generates a functional component).

That's it. No global CLI tools, no extra frameworks. You're ready to create your first project.

One note: if you're on Windows, I strongly recommend using Windows Terminal (from the Microsoft Store) and either PowerShell or WSL2 (Windows Subsystem for Linux). It will save you headaches with path separators and permissions later.

Step 2: Create the React Project with Vite

In 2026, Create React App is officially a zombie. The React team itself recommends Vite for new projects. Here's how to scaffold a new React app:

Open your terminal, navigate to the folder where you keep your projects (mine is ~/projects), and run:

npm create vite@latest my-react-app -- --template react

This downloads the latest Vite template for React. You'll see a few prompts—just hit Enter to accept the defaults. Once it finishes, move into the project folder and install dependencies:

cd my-react-app
npm install

Now start the development server:

npm run dev

Open your browser to http://localhost:5173 (Vite's default port). You should see a Vite + React starter page. That's your app running.

Let's talk about the folder structure Vite created for you:

  • public/ — Static assets like images and favicons. Files here are copied as-is to the build output.
  • src/ — Your source code. main.jsx is the entry point; App.jsx is your root component.
  • package.json — Dependencies and scripts. Notice there's no react-router or axios yet—we'll add those later.
  • vite.config.js — Vite's configuration. You'll rarely need to touch it, but it's clean and simple.

The beauty of Vite is speed. Hot module replacement (HMR) happens in milliseconds, even as your app grows. I've built apps with hundreds of components, and the dev server never stutters.

Step 3: Build Your First Component and Style It

Open src/App.jsx and replace its contents with a simple functional component:

import { useState } from 'react'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    

Welcome to My React App

You clicked {count} times

) } export default App

This is a classic counter example, but it demonstrates three core React concepts: JSX (HTML-like syntax inside JavaScript), state (the count variable managed by useState), and event handling (the onClick prop).

Now for styling. In 2026, Tailwind CSS is the default choice for most developers because it's fast and doesn't require leaving your JSX. Install it with:

npm install -D tailwindcss @tailwindcss/vite

Then add the Tailwind plugin to vite.config.js:

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

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

Replace the contents of src/App.css with:

@import "tailwindcss";

Now you can use utility classes directly in your components:

If Tailwind isn't your style, CSS Modules work out of the box with Vite—just name your files Component.module.css and import them. I've used both approaches in production, and I lean toward Tailwind for speed and CSS Modules for larger teams where design tokens need strict enforcement. For this guide, Tailwind is simpler.

Step 4: Add Routing with React Router v7

Your app currently has one page. To add multiple pages (like a Home page and an About page), you need a router. React Router v7 is the current stable version and works seamlessly with Vite.

Install it:

npm install react-router-dom

Now update src/main.jsx to wrap your app in a BrowserRouter:

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
  
    
      
    
  
)

Create two new component files: src/pages/Home.jsx and src/pages/About.jsx. Each should be a simple functional component. Then replace App.jsx with:

import { Routes, Route, Link } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'

function App() {
  return (
    
} /> } />
) } export default App

Now you have client-side routing. Click the links and the URL changes without a page refresh. React Router v7 also supports nested routes, loaders, and actions (similar to Remix), but for a basic app, this pattern is all you need.

One trade-off: file-based routing (like Next.js) is more convenient because you don't have to update a central routes file. But manual routing gives you total control over layout and transitions. I prefer it for apps with fewer than 10 pages.

Step 5: Manage State and Fetch Data

Most apps need to communicate with a backend or an API. Let's fetch a list of placeholder posts from JSONPlaceholder and display them.

Create src/pages/Posts.jsx:

import { useState, useEffect } from 'react'

function Posts() {
  const [posts, setPosts] = useState([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(res => {
        if (!res.ok) throw new Error('Failed to fetch')
        return res.json()
      })
      .then(data => {
        setPosts(data.slice(0, 10)) // first 10 posts
        setLoading(false)
      })
      .catch(err => {
        setError(err.message)
        setLoading(false)
      })
  }, [])

  if (loading) return 

Loading...

if (error) return

Error: {error}

return (
    {posts.map(post => (
  • {post.title}

    {post.body}

  • ))}
) } export default Posts

Add a route for it in App.jsx: <Route path="/posts" element=<Posts /> /> and a link in the nav.

This pattern—useEffect for side effects, useState for loading/error/data—is the bread and butter of React data fetching. It's not perfect (you'd use a library like React Query or SWR for caching and retries in production), but it teaches you the fundamentals.

If your app grows beyond a few components, you'll want a global state solution. The Context API is built into React and works well for themes, auth state, or user preferences. For more complex state (like a shopping cart with multiple updates), Zustand is my go-to—it's tiny, has no boilerplate, and doesn't wrap your app in providers.

Here's my rule of thumb: if you're passing props through more than three levels, reach for Context. If you're sharing state across unrelated components, reach for Zustand.

Step 6: Test and Optimize Before Deployment

Testing isn't glamorous, but skipping it is how you deploy a broken app at 2 AM. Vite ships with Vitest, a testing framework that's nearly drop-in compatible with Jest but faster. Install it:

npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom

Add a test script to package.json:

"scripts": {
  "test": "vitest"
}

Create src/App.test.jsx:

import { render, screen } from '@testing-library/react'
import App from './App'

test('renders home link', () => {
  render()
  const linkElement = screen.getByText(/Home/i)
  expect(linkElement).toBeInTheDocument()
})

Run npm test—you should see a passing test. For real-world apps, aim to test user interactions and critical paths, not implementation details.

Now optimize. Two quick wins:

  1. Lazy load routes so users don't download code they don't need. Replace import Posts from './pages/Posts' with:
const Posts = React.lazy(() => import('./pages/Posts'))

Then wrap your <Routes> in a <Suspense fallback=<div>Loading...</div>> component.

  1. Add a key prop to every list item (you already did in the Posts component). This helps React efficiently re-render only changed items.

These two changes alone can cut your initial bundle size by 30-50% on multi-page apps.

Step 7: Deploy to Production (Free Options)

You've built a functioning React app. Now put it on the internet where people can see it. Vercel and Netlify both offer free hosting with automatic deployments from GitHub.

Here's the Vercel path (my personal favorite because it's one click):

  1. Push your project to a GitHub repository (git init, git add ., git commit -m "first commit", then create a repo on GitHub and push).
  2. Go to vercel.com and sign up with GitHub.
  3. Click Add New Project, select your repo, and click Deploy.
  4. Vercel auto-detects Vite and sets the build command to vite build and the output directory to dist. Click Deploy again.

Within a minute, you'll have a live URL like https://my-react-app.vercel.app. Every time you push to the main branch, Vercel rebuilds and redeploys automatically.

For Netlify, the process is nearly identical: connect your GitHub repo, set the build command to npm run build and the publish directory to dist, then deploy.

One gotcha: if you're using client-side routing (React Router), your deployed app might show a 404 on routes like /about when accessed directly. To fix this, add a _redirects file to the public folder with this line:

/*    /index.html   200

This tells the hosting server to serve index.html for all paths, letting React Router handle the routing client-side. On Vercel, you can also add a vercel.json file with the same redirect rule.

Common Pitfalls and How to Avoid Them

Over the years, I've seen beginners (and myself) trip over the same few issues. Here are the most common and how to fix them fast:

  • Broken imports — Double-check file paths. A missing ./ or a typo in the component name leads to a red-screen error. Use VS Code's import autocomplete (Ctrl+Space) to avoid this.
  • Missing key props in lists — React will warn you in the console. Always use a unique, stable identifier (like a database ID), not the array index.
  • useEffect dependency array mistakes — If you forget to include a variable that useEffect uses, you'll get stale data or infinite loops. The React linting rules (eslint-plugin-react-hooks) catch this automatically—install it and fix warnings.
  • Forgetting to set a base URL for deployment — If your app is served from a subpath (like /my-app), configure Vite's base in vite.config.js (e.g., base: '/my-app/'). Otherwise, assets won't load.
  • State updates not reflecting immediately — Remember that setState is asynchronous. If you need the new state right away, use a useEffect that depends on that state, or use the functional updater form: setCount(prev => prev + 1).

These mistakes are normal. Every React developer has made them. The key is knowing where to look when something breaks.

Your move now. Open your terminal, run the commands from Step 2, and build something that matters to you. The 7 steps above are your blueprint—customize them, break things, and learn. If you get stuck, the React documentation and Vite documentation are excellent. And if you found this guide useful, bookmark it for your next project—you'll thank yourself when you need to set up routing or deployment in a hurry.