Doing React the Right Way - 2/10
Setting Up React & TypeScript: The Modern Way
You understand why React exists and the problems it solves. Now comes the practical part that separates theory from practice: setting up a modern development environment. This is your foundation—get this right, and everything else becomes easier. Get it wrong, and you’ll spend weeks fighting configuration issues instead of learning React.
We’re not using yesterday’s tools. We’re building with the current state-of-the-art setup that prioritizes speed, developer experience, and maintainability.
The Rise and Fall of create-react-app
Let’s start by understanding what we’re not using and why. For years, create-react-app
(CRA) was the undisputed champion for React project setup. One command gave you everything you needed:
npx create-react-app my-app --template typescript
CRA handled all the complex configuration behind the scenes, giving millions of developers an instant entry point into React without requiring deep knowledge of build tools. It was revolutionary for its time.
But the web development landscape moves fast, and CRA’s foundation—Webpack—became its weakness.
Why Webpack-Based Tools Became Slow
CRA was built entirely on Webpack, which made sense when it was created. But Webpack’s architecture reflects the constraints of an earlier era:
The Full-Meal Approach: When you run npm start
with CRA, Webpack must:
- Crawl your entire application, analyzing every import statement
- Build a complete dependency graph of every module in your project
- Transpile every JavaScript, TypeScript, and CSS file through Babel/TypeScript compiler
- Bundle everything together into optimized chunks
- Finally serve your application
On small projects, this is acceptable. On real-world applications with hundreds of components and dozens of dependencies, this startup process can take 30 seconds to several minutes. Your development feedback loop dies before you even start coding.
Even worse is Hot Module Replacement (HMR). Change one CSS property, and Webpack often recalculates large portions of your bundle. You’re left waiting for changes to appear while your creative momentum evaporates.
Enter Vite: The Performance Revolution
Vite (pronounced “veet,” French for “fast”) represents a fundamental shift in development tooling philosophy. Instead of “bundle everything first,” Vite asks: “What if we bundled nothing during development?”
Vite’s speed comes from two key insights that leverage modern browser capabilities:
Native ES Modules in Development
Modern browsers natively understand import
statements. Vite exploits this fact brilliantly:
Instead of pre-bundling your entire application, Vite’s dev server starts instantly and serves files on demand. When your browser requests the main entry point, it receives that file. The browser then reads the import statements and makes additional HTTP requests for each imported module:
// main.tsx - served directly to browser
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx"; // Browser makes another request for this
// Browser effectively becomes the bundler
The result? Instant server startup—literally under a second regardless of project size.
Lightning-Fast Transpilation with esbuild
Vite only transpiles files when the browser actually requests them. Working on the homepage? Your settings and profile components are never touched until you navigate to them.
For transpilation, Vite uses esbuild—a JavaScript compiler written in Go, not JavaScript. This architectural choice makes esbuild 10-100x faster than traditional JavaScript-based tools like Babel.
Combined with on-demand processing, Hot Module Replacement becomes nearly instantaneous. Changes appear on screen so fast it feels like editing a static HTML file.
Your React Development Environment
Let’s build your setup. Open your terminal and run:
npm create vite@latest my-react-app -- --template react-ts
This command breaks down as:
npm create vite@latest
: Runs the latest version of Vite’s project scaffolding toolmy-react-app
: Your project folder name (choose whatever you prefer)--
: Separator telling npm to pass the following arguments directly to create-vite--template react-ts
: Creates a React project with TypeScript support
Navigate to your project and install dependencies:
cd my-react-app
npm install
npm run dev
Your development server starts instantly. Open the provided localhost URL to see your running React application.
Understanding Your Project Structure
Let’s examine what Vite created for you:
my-react-app/
├── node_modules/ # Dependencies (don't touch this)
├── public/ # Static assets
│ └── vite.svg
├── src/ # Your application code
│ ├── assets/
│ │ └── react.svg
│ ├── App.css
│ ├── App.tsx # Main component
│ ├── index.css
│ ├── main.tsx # Application entry point
│ └── vite-env.d.ts # TypeScript environment declarations
├── .gitignore
├── index.html # The single HTML file for your SPA
├── package.json # Project metadata and scripts
├── package-lock.json # Dependency lock file
├── tsconfig.json # TypeScript configuration
└── vite.config.ts # Vite configuration
Key Files Explained
package.json
: Your project’s identity and dependency manifest
dependencies
: Packages needed for production (react, react-dom)devDependencies
: Development-only packages (vite, typescript)scripts
: Command shortcuts—npm run dev
executesvite
package-lock.json
: Records exact versions of all dependencies and sub-dependencies. This ensures identical node_modules
across different machines and prevents “works on my machine” issues. Never edit this file manually.
tsconfig.json
: TypeScript compiler configuration. Vite provides sensible defaults:
"target": "ESNext"
: Compile to modern JavaScript (Vite handles browser compatibility)"jsx": "react-jsx"
: Use React’s modern JSX transform"moduleResolution": "bundler"
: Modern module resolution matching bundler behavior
You’ll return here when adding libraries that lack proper TypeScript definitions.
index.html
: The foundation of your SPA. Contains two crucial elements:
<div id="root"></div>
: Where your entire React application renders<script type="module" src="/src/main.tsx"></script>
: The entry point for your JavaScript application
src/main.tsx
: The bridge between static HTML and dynamic React:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
This file:
- Finds the
root
div in your HTML - Creates a React root for modern concurrent features
- Renders your
App
component insideReact.StrictMode
App.tsx
: Your first React component—a function that returns JSX describing your UI.
vite.config.ts
: Vite’s configuration file. Currently minimal, but where you’ll add plugins and customize build behavior as your needs grow.
Why React and ReactDOM Are Separate
You might wonder why you need both react
and react-dom
packages. This separation is one of React’s most important architectural decisions.
React: The Platform-Agnostic Engine
The react
package knows nothing about web browsers, DOM elements, or HTML. It’s a pure, abstract system for:
- Creating and managing components
- Handling state and lifecycle
- Building Virtual DOM trees in memory
Think of React as an architect who creates detailed blueprints but never touches actual construction materials.
ReactDOM: The Web-Specific Builder
The react-dom
package is a renderer—it knows how to take React’s abstract component descriptions and turn them into real DOM elements. It handles:
- Converting Virtual DOM to actual browser DOM
- Efficiently updating the DOM when state changes
- Managing event systems and browser-specific behavior
The Multi-Platform Advantage
This separation enables React’s multi-platform power:
- Web applications:
react
+react-dom
- Mobile applications:
react
+react-native
(renders to native iOS/Android components) - Command-line tools:
react
+ink
(renders to terminal interfaces) - PDF generation:
react
+react-pdf
- VR experiences:
react
+react-360
You learn React’s component model once and can apply it across platforms by swapping renderers. This architectural decision transformed React from a web library into a universal UI paradigm.
Development Workflow Excellence
Your setup provides several productivity features:
Hot Module Replacement: Changes appear instantly without losing component state. Modify a component’s styling or logic, and see updates immediately.
TypeScript Integration: Full type checking without configuration. Catch errors at development time instead of runtime.
Modern JavaScript: Write the latest JavaScript features without worrying about browser compatibility.
Optimized Production Builds: npm run build
creates optimized, minified bundles perfect for deployment.
Next Steps
You now have a professional React development environment that prioritizes speed and developer experience. Your setup includes:
- Lightning-fast development server with instant HMR
- Full TypeScript support with no configuration
- Modern build tooling optimized for both development and production
- A clear project structure that scales with your application
In our next article, we’ll dive into React’s core concepts: components, JSX, and props. But your foundation is solid—you’re working with tools that respect your time and help you focus on learning React rather than fighting configuration issues.
The difference between a good developer environment and a great one isn’t just convenience—it’s the difference between spending your time learning React and spending it waiting for builds to complete.
You’re ready to start building.