Skip to content

Application migration

React to Next.js Migration

Moving a Create React App or Vite SPA to Next.js - route by route, with the client-side data layer converted to server rendering and the SEO the SPA never had.

A single-page application and a Next.js application are not the same shape. The SPA renders an empty div and fills it from the browser; Next.js renders on the server and sends HTML. Everything that makes the migration interesting follows from that one difference, and none of it is a file move.

This is the migration for teams on Create React App, Vite, or a hand-rolled React setup - not for teams already on the Pages Router, which is a different problem with a different path.

What actually changes

Routing. React Router's component-based routes become a file tree. A <Route path="/products/:id"> becomes app/products/[id]/page.tsx. Nested routes with shared chrome become nested layouts, which is usually simpler than what was there.

Data fetching. This is the bulk of the work. A SPA fetches in useEffect after mount, which means a loading spinner, a waterfall, and nothing for a crawler to read. In Next.js the same fetch runs on the server before the response is sent. The component stops managing loading state and starts being an async function.

The bundle. Everything in a SPA is client code by definition. After the migration, most of it does not need to be - and the routes we move typically lose 40-60% of their JavaScript, because the tree only has to ship what is actually interactive.

Browser-only assumptions. window, localStorage and document at module scope crash on the server. Finding them is mechanical; deciding which belong in a client component and which were never needed is the judgement.

The order we work in

Next.js and your SPA can serve from the same domain during the migration, so nothing has to happen at once.

  1. The shell and one leaf route. Establishes the layout, the metadata helper and the data conventions on something low-risk.
  2. The routes search engines care about. Marketing pages, product pages, anything public. This is where the return is, because these are the pages that had no server-rendered HTML at all.
  3. Authenticated routes. Dashboards and settings. Often these stay mostly client-rendered and that is correct - there is no SEO value in a page behind a login, and the App Router is happy to leave it interactive.
  4. The old build, deleted. Not before.

What you should expect to gain

The honest version: the performance win is real but uneven, and the SEO win is usually the larger one.

A SPA's first paint waits for the bundle to download, parse and execute before anything appears. Server rendering removes that entirely from the critical path, which is why LCP moves most on first visits and cold caches, and least for a returning user with a warm cache.

The SEO change is more categorical. A crawler receiving an empty div and a script tag has nothing to index. After the migration it receives the content. For applications whose public pages were invisible, that is not an optimisation - it is the difference between existing in search results and not.

What we need to see first

The repository, or a list of routes and what each one fetches. Most of the estimate is in the data layer, not the component count: a hundred presentational components convert in a week, and one tangled global store can take longer than all of them.

What you receive

The application on Next.js, moved route by route, with the phases and a route count for each agreed before any of them starts. Every phase is reviewable on its own and leaves a working application behind it.

The data layer changes most, so you get it documented: what moved to the server, what stayed on the client and why, and the useEffect calls that turned out to be doing something nobody remembered.

Scope and terms

Engagement model
Fixed scope, agreed in writing before work starts. Not a day rate against an open backlog.
Price and timeline
Both are set per project, once the scope is. Quoted together, before anything is built.
What we need from you
One person who can approve decisions, and access to your repository and issue tracker.
Not included
Anything outside the agreed scope. It becomes its own scope rather than a variation order.
Third-party costs
Hosting, licences, API fees and SaaS subscriptions are contracted and paid by you.
Invoicing
Codefacture Yazılım A.Ş., Türkiye. EUR, USD or GBP by bank transfer, with no Turkish VAT on exported services.

Frequently asked questions

Do you rewrite or migrate?
Migrate, route by route, with both applications running until the last route moves. A rewrite means a long stretch where nothing ships, and that is how these projects die.
Can we keep shipping features during it?
Yes, and you should. The route-by-route order exists so your team keeps working in the parts that have not moved yet.
What actually improves?
The first load, because data arrives with the HTML instead of after it. Search visibility, because there is finally markup to index. And a data layer that stops being a pile of useEffect calls. A slow API does not improve on its own; the migration makes it more visible, not faster.
How long does it take?
It depends on the route count and how tangled the data layer is with the components. After reading the repository we size it in phases with a route count for each, so you can stop after any phase and still have a working application.
Call us+1 848 272 7583WhatsApp+90 850 308 5436Emailinfo@codefacture.comContact page