Revision: guidance for maintaining or migrating an existing Create React App application.

Next.js versus Create React App is now an existing-application decision. On 14 February 2025, the React team deprecated CRA for new applications, encouraged existing projects to migrate and said CRA would continue in maintenance mode. That does not make an existing deployment stop working, or justify rewriting every component immediately.

React is the UI library used by both. CRA supplies an older development and build toolchain; Next.js adds application conventions around React. The useful question is which responsibilities your replacement should take on, while preserving the behavior users already depend on.

Choose the outcome before choosing migration steps

Evaluate three directions against the existing application's constraints. Temporary maintenance is a time-bounded decision with an owner and review date, not an assumption that every dependency will remain supported.

Three decisions with different scope
DirectionReason to consider itResponsibility to retain or add
Maintain CRA temporarilyA critical release or unresolved dependency makes an immediate tooling move impractical.Review dependencies and runtime support, keep a reproducible build, record constraints and set a migration checkpoint.
Move the client SPA to ViteThe existing router and external API meet the need; the immediate goal is replacing build tooling.Vite is a build tool. The team still owns routing, data access, testing and deployment integration.
Move toward Next.jsThe roadmap needs integrated routing, rendering choices or server capabilities worth adopting.Plan framework conventions and hosting requirements; introduce those changes in reviewable stages.

Consider a fictional customer portal with an established client router and separate API. Replacing its bundler could be sufficient for the immediate problem. A planned public catalogue may justify evaluating framework rendering later. Neither decision requires replacing all working UI components at once.

If the decision also includes changing the UI library, use the broader Angular, React and Vue comparison or the deeper React and Vue guide. Keep that separate from a CRA toolchain migration.

Inventory the application you actually have

Start with the repository revision, package manager, lockfile and deployed runtime. Record how developers and CI build the application, including custom wrappers, ejected configuration and scripts outside the package manifest. Identify the current build artifact and the host that serves it.

The following read-only npm package inspection command reads selected declarations in the current directory's package.json:

npm pkg get scripts dependencies.react-scripts devDependencies.react-scripts dependencies.next devDependencies.next dependencies.vite devDependencies.vite

We ran this command against a clearly fictional CRA manifest. It reported react-scripts start, build and test script strings, plus a declared react-scripts value of 5.0.1. No dependencies were installed and those scripts were not executed. The declaration is not an observed installed version or evidence that the application builds.

The frontend evaluation workbook (Markdown) includes the exact inspection output and a migration checklist. For a real project, inspect the lockfile and relevant workspace manifests as well; this one command cannot establish the resolved dependency tree or compatibility.

Record the behavior and owner behind each configuration
AreaWhat to inventory
Routes and accessRouter version, nested URLs, redirects, login callbacks, refresh behavior and server authorization.
Configuration and APIPublic environment names, API origins, development proxy rules, cookies and deployment-specific values.
Assets and browser featuresPublic files, imported images and SVGs, fonts, browser-only libraries and service-worker registration.
Checks and build hooksTests, mocks, transforms, linting, type checks, custom bundler configuration and pre/post-build work.
HostingOutput directory, subpath, cache rules, deep-link fallback, error pages and release/recovery procedure.

Include a baseline failure list. A broken test or undocumented proxy should remain visible rather than being attributed to the new toolchain later. Assign an owner to each unknown that could block the migration.

Make configuration changes explicit

Browser-public environment variables need careful mapping. CRA exposes custom REACT_APP_ values; Vite uses VITE_ by default; Next.js can inline NEXT_PUBLIC_ values into browser JavaScript. These are public configuration mechanisms, not secret storage. Renaming a credential with a new prefix does not protect it.

For each variable, record where it is read, when it becomes part of an artifact and how staging differs from production. Keep server credentials at the appropriate server boundary. Review API URLs and authentication flows alongside environment names, rather than performing a blind prefix replacement.

Decide where each development proxy rule will live after deployment. A local server forwarding API requests does not establish that a static production host does the same. Similarly, public asset paths and a deployment under /portal/ need explicit checks against the chosen host.

Separate compatibility from new rendering behavior

The official Next.js CRA migration guide starts with a client-only SPA arrangement that retains the existing router, then introduces framework features incrementally. That is a useful staging principle: establish compatibility before changing route ownership and data loading.

Next.js is not limited to request-time server rendering. Distinguish when HTML is generated, where component code executes and whether the deployment needs a running application server. Static generation can produce files during a build; dynamic request handling requires a suitable runtime.

Check Next.js static-export restrictions before choosing that output. Static files do not provide Next.js server rewrites, request-dependent cookies, Server Actions or the default image optimizer. A host can supply its own routing rules, but that does not turn exported files into a Next.js server. Do not combine a static export with server-only proxy instructions and assume both will work.

Browser-only dependencies need their own review. As the Next.js component guide explains, a Client Component can still be prerendered. Adding 'use client' alone does not make every window or document access safe. Use an appropriate browser execution boundary and verify the selected dependency's behavior there.

Choose rendering changes route by route after the initial move. Public content and an authenticated workspace may have different needs. Server rendering is not an automatic performance or search-ranking improvement; measure the behavior that motivated the change.

Propose a compatibility check before declaring completion

For the fictional portal, a bounded evaluation would preserve its existing API and use representative synthetic accounts and records. The following checks are proposed migration acceptance work; no CRA, Vite or Next.js application was migrated or benchmarked for this article.

  • Navigation: open a nested URL directly, refresh it, navigate back and verify query parameters and unknown-route behavior.
  • Access and data: exercise sign-in, expiry and sign-out; check that the API or server route enforces authorization independently of client navigation.
  • Deployment: inspect the actual production build under its intended subpath, with correct assets and API requests.
  • Existing checks: run the required tests and inspect changed transforms, mocks and test commands instead of assuming CRA's setup transfers unchanged.
  • Browser behavior: check key interactions, loading and error states, keyboard use and any service-worker update behavior.

Record the tested revision, environment, results and remaining gaps. A successful build cannot establish that login redirects, cached clients or browser interactions work. If rendering changes later, add appropriate initial-HTML, hydration and personalized-data checks to that separate change.

Leave a release and maintenance decision

Identify the artifact to release, the configuration it needs, the owner approving it and the observation that would stop rollout. Keep the previous compatible artifact available, and account for API or data changes that a frontend rollback would not undo.

The migration should leave updated setup notes, scripts, deployment instructions and a follow-up list with owners. For a framework move, bring that scope to a Next.js development discussion. For a narrower toolchain assessment, our JavaScript development page provides the relevant starting point.