Volume I index
Project Tooling and the First Milestone

Create React App Deprecation and Modern Starts

Ishtmeet Singh @ishtms/July 14, 2026/8 min read
#react#create-react-app#vite#frameworks#migration

Create React App is deprecated for new applications. Choosing its replacement requires understanding which responsibilities a project starter accepts for you, not merely memorizing another command.

React is a UI library. A complete application also needs decisions about development transforms, production output, routing, data loading, deployment, and sometimes server rendering. Create React App once supplied a widely shared build-tool answer for new client-rendered React applications. It is now deprecated for new apps and remains in maintenance mode.

Existing Create React App projects do not stop running because of that status. New projects should begin from a current path, while existing ones need a deliberate migration based on their actual constraints.

Recognize a Create React App Project

Open package.json and look for react-scripts.

json
{
  "dependencies": {
    "react-scripts": "5.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build"
  }
}

The filenames alone are not enough. Both old and current projects can have src/App.jsx, public, and an npm start command. react-scripts is the direct signal.

Create React App hid much of its build configuration behind that package. A project could run, test, and build without maintaining a visible bundler configuration. The tradeoff was that changing the toolchain outside supported options became difficult.

Some projects used npm run eject to copy the hidden configuration into the repository. An ejected project no longer has the same migration surface as an untouched Create React App project. Its repository contains a large webpack, Babel, Jest, and ESLint configuration that may have been edited over time.

What Deprecation Means Here

The React team deprecated Create React App for new apps in February 2025. The package continues in maintenance mode, and a new installation displays a deprecation warning.

Deprecation is guidance about the future path. It is not a remote shutdown switch. A built static application keeps serving its files. A local project can continue to install and run while its dependency graph remains compatible with the surrounding Node.js and package environment.

The risk grows over time. A fixed toolchain can accumulate outdated transitive packages, lag current bundler behavior, and become harder to combine with current React and platform features. The migration cost also grows when application code relies on tool-specific assumptions.

Why React Recommends Frameworks for Many New Apps

The current React documentation recommends starting new applications with a framework. A framework can coordinate responsibilities that are otherwise selected one library at a time.

Depending on the framework and deployment mode, those responsibilities can include these features.

  • URL routing and nested layouts
  • route-level data loading
  • code splitting by route
  • server rendering or prerendering
  • mutation handling
  • metadata and resource loading
  • production deployment adapters

Those are not features of useState or JSX. They sit around React and determine how an application reaches users.

React's current app-start guide lists framework paths including Next.js App Router and React Router framework mode.

Starting with a framework does not mean every component runs on a server. It means the framework makes more application-level decisions and can offer several rendering modes.

Why This Project Uses Vite

Vite is a build tool and development server, not a full React application framework. It keeps the client entry visible.

text
index.html
  -> main.tsx
     -> createRoot
        -> App

That visibility serves the teaching sequence. You can inspect the root container, module graph, development server, and production output before a framework introduces route modules, server entries, or generated conventions.

A Vite scratch setup can also be a valid product choice when the application is client-rendered and the team is prepared to maintain routing, data loading, deployment, and other missing policies.

The phrase "scratch setup" does not mean writing a bundler. It means assembling an application from a build tool and selected libraries rather than accepting a framework's integrated model.

Separate React from the Starter

The component code is not Vite-specific.

tsx
function LessonCard({ title }: { title: string }) {
  return <h2>{title}</h2>;
}

Props, state, JSX, event handling, and rendering are React concepts. File discovery, environment variable names, development commands, and build output are supplied by tooling.

This separation helps when reading an unfamiliar repository.

text
React concern: component returns UI from props
Vite concern: .tsx is transformed and served
framework concern: route decides when component appears
host concern: browser creates DOM and handles input

When a behavior changes after migration, identify the layer that implemented it. A component prop bug is not repaired with a Vite plugin. A changed asset base path is not repaired with useState.

Choose a Start from Requirements

A small client-only internal tool may need static hosting, browser APIs, and one API endpoint. Vite plus a router can fit.

A public content or commerce application may need server rendering, route data, metadata, caching, and server-side mutations. A framework can supply one coordinated model for those needs.

An embeddable widget may need to attach React to one region of an existing page. A scratch client root and library build may fit better than a full-site framework.

A reusable component package is not an application. It needs a library build, package exports, type declarations, and a separate demonstration environment.

No starter name answers all four situations. Write the constraints first.

text
rendering: client only, server, static, or mixed
routing: none, browser routes, or server routes
data: local, API, route loaders, or server reads
deployment: static files, Node server, edge runtime, or platform adapter
maintenance: which team maintains each added library

Audit an Existing Create React App Before Migration

Begin with a build that can be reproduced.

bash
npm ci
npm test -- --watchAll=false
npm run build

The exact test command can differ if the project changed its scripts. Record successful commands and their environment.

Then inspect the behavior tied to Create React App.

Environment variables

Create React App exposes custom client variables prefixed with REACT_APP_.

env
REACT_APP_API_ORIGIN=https://api.example.com

Vite uses VITE_ by default and reads through import.meta.env.

env
VITE_API_ORIGIN=https://api.example.com

This is a source migration and a deployment configuration migration. Renaming it only in local .env leaves hosted environments unchanged.

HTML placeholders

Create React App projects may use placeholders such as %PUBLIC_URL% in public/index.html. Vite uses index.html at the project root and has different HTML handling.

Development proxy

A CRA project may contain a proxy field in package.json or use custom proxy middleware. Record every forwarded path and cookie assumption before configuring a Vite server proxy.

Asset imports

Check imports, public URLs, SVG component transforms, and CSS behavior. Toolchains can support different import conventions even when the files have the same extensions.

Tests and service workers

Create React App commonly paired with Jest configuration and optional service worker code. Vite does not convert those automatically. A real migration must preserve its existing checks from the start.

Migrate the Tooling Without Rewriting the App

A tool migration already changes many variables. Keep the first application-code changes mechanical.

  1. Create the Vite entry index.html.
  2. Add Vite and its React plugin.
  3. move the existing source entry into the Vite graph.
  4. Convert environment access and HTML placeholders.
  5. Recreate required proxy and asset behavior.
  6. Run the existing tests through a maintained test setup.
  7. compare the new production output with the last known CRA build.

Do not combine this work with a router rewrite, state library replacement, CSS rewrite, and component redesign unless the project has no safer path. Separate changes make regressions attributable.

Do Not Use eject as a Migration Step

Ejecting copies the old configuration into the repository. It increases the amount of toolchain code the team must maintain and cannot be reversed by another script.

If an unejected project needs to leave Create React App, migrate directly to the selected current tool or framework. If a project was already ejected, treat its configuration as a custom build system and audit the actual files.

A Modern Start Is a Maintained Start

"Modern" does not mean selecting the newest package published this morning. It means the project uses supported release lines, current documented setup, explicit checks, and dependencies the team can maintain.

For this project, that means React 19.2.x, Vite 8, a supported Node.js release, TypeScript checking, ESLint, and a production build.

Source Notes