Let's look at how to properly set up a React environment today without falling into legacy tooling traps.
What you'll learn
- Why you should skip legacy tools like
create-react-app - How to choose between full-stack frameworks and custom setups
- Setting up a minimal local development container
- Bootstrapping your first production-ready component
Why create-react-app Is Dead
If you search older tutorials, you will see npx create-react-app my-app everywhere. Ignore it. That tool is officially deprecated and unmaintained.
Modern React relies heavily on server-side rendering, routing integration, and asset optimization that basic single-page boilerplate templates never handled well. Today, you either adopt a full framework or assemble a custom bundler setup using Vite.
# Don't do this anymore (deprecated)
npx create-react-app my-app
# Do this instead for a modern Vite + React setup
npx create vite@latest my-react-app -- --template react-ts
Speed Note: Vite starts local dev servers almost instantly because it leverages native ESM in the browser during development instead of bundling your entire node_modules up front.
Choosing Your Architecture: Framework vs Scratch
React is designed for gradual adoption, but your project structure depends entirely on your scale. If you are building a full web application, building from scratch means reinventing routing, code-splitting, and data fetching.
Here is how to make the call:
// App.tsx - A minimal entry point for a Vite-powered client app
import { useState } from 'react';
export default function App() {
const [count, setCount] = useState<number>(0);
return (
<main className="p-8 max-w-md mx-auto font-sans">
<h1 className="text-2xl font-bold mb-4">React Local Setup</h1>
<button
onClick={() => setCount(c => c + 1)}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition"
>
Clicks: {count}
</button>
</main>
);
}
Gotcha: If your app needs SEO or fast initial page loads behind an auth wall, reach for Next.js, Remix, or Expo instead of a plain client-side Vite shell.
Adding React to an Existing Legacy App
Sometimes you don't need a greenfield rewrite. You just need a React widget embedded inside an older server-rendered app (like Rails, Laravel, or a static HTML multi-page site).
Instead of rendering a root for the whole page, target specific DOM nodes:
// widget-entry.tsx - Mounting React inside a legacy DOM container
import { createRoot } from 'react-dom/client';
function StatusWidget({ status }: { status: string }) {
return (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
System: {status}
</span>
);
}
// Find the target container injected by your backend template
const container = document.getElementById('react-status-root');
if (container) {
const root = createRoot(container);
root.render(<StatusWidget status="Operational" />);
}
Accessibility Tip: When mounting React into foreign HTML structures, make sure your container elements preserve valid ARIA attributes and don't break existing tab-index flows.
Configuring TypeScript and Strict Mode
Always initialize your React apps with TypeScript enabled from day one. Catching prop mismatches at compile time saves hours of production debugging.
Make sure your main.tsx wraps your root component in StrictMode to catch lifecycle side-effects early during development.
// main.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import './index.css';
const rootElement = document.getElementById('root');
if (!rootElement) {
throw new Error('Failed to find the root element');
}
createRoot(rootElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Summary
Setting up React in modern development comes down to matching your tooling to your deployment target. Skip deprecated boilerplates, use Vite for client apps, and lean on frameworks when you need server-side rendering.
Next Steps
- Dive into the React Quick Start guide to review core state and props patterns.
- Explore component composition and custom hooks for scalable logic reuse.