Skip to content

Comparison

React vs Next.js

Every Next.js application is a React application, so the real question is narrower than the search suggests: React on its own, usually built with Vite, or React inside a framework that adds routing, server rendering and a deployment model. Search visibility, hosting and your team’s appetite for running a server usually decide it.

What the choice is actually between

The confusion behind this search is reasonable, and clearing it up is most of the answer. React is a library for building user interfaces out of components. It decides how a component describes its output, how state changes flow through the tree, and how the result is reconciled with what is already on the page. It does not decide which URL shows which screen, how data is loaded before a page renders, where the HTML is produced, how the code is bundled or how any of it is deployed. Next.js is a framework, built by Vercel, that answers all of those questions on top of React. When you write a Next.js application you are writing React components, and the framework supplies file-based routing, a rendering strategy per route, React Server Components, data fetching and mutation patterns, server-side code next to the interface, image and font handling, and a build and deployment model.

So the choice is between using React on its own and using React inside Next.js. "On its own" has also changed meaning in the last couple of years. Create React App, the old default starting point, was deprecated by the React team in early 2025, and a plain React project today is usually built with Vite: a fast development server plus a build step that turns your code into a folder of static files. You add a router, a data fetching library and whatever else the application needs, and the browser does all of the rendering. The React documentation now suggests starting most new applications with a framework, while still documenting the build-it-yourself route for teams whose needs differ. That recommendation is a fair signal about where the ecosystem is heading. It is not a verdict on your particular project.

We build with both, and the site you are reading is a Next.js application, so we have no reason to push you either way. The question that settles most of these decisions is a concrete one: who needs to read your pages before any JavaScript has run? Search engines do, link previews in chat and social apps do, and first-time visitors on slow phones effectively do. Logged-in users of an internal tool, people using a widget embedded in somebody else’s site, and anyone running a desktop app built on web technology do not. If the first group matters to the business, Next.js earns its extra complexity. If only the second group exists, plain React is simpler to build, cheaper to host and easier to hand over.

The short answer

Use plain React, built with Vite or a similar tool, when the application lives behind a login, has no search requirement, runs inside someone else’s page or a desktop shell, or is the front end to a backend you already have. You get static files that any host or CDN can serve, no server process of your own to run, one runtime to reason about, and a development loop with very few moving parts. For a large class of software, including most admin panels, internal tools, dashboards and data-heavy applications, that is the better engineering choice, and adding a server rendering framework would buy complexity for benefits nobody receives.

Use Next.js when public pages have to rank and preview properly, when first-load speed on ordinary phones affects revenue, or when you want the interface and its server-side data access in one codebase owned by one team. Go in knowing the costs: a steeper learning curve around the server and client component boundary and the caching layers, a Node.js runtime to operate unless you restrict yourself to static export, framework lock-in at the routing and data layers, and a noticeable pull towards the hosting platform run by the company that builds the framework. Those costs are worth paying for a public product. They are dead weight for a tool that only logged-in staff will ever see.

The awkward middle is also the most common case: a SaaS product with a public marketing site in front of a logged-in application. There are two sound answers. One Next.js application can serve both, sharing a design system, authentication and a deployment. Or the marketing site can be static or Next.js while the application is a plain React single-page app on its own subdomain, which is often cleaner than people expect, because the two halves have different owners, different release rhythms and different performance needs. The two choices to avoid are picking Next.js only because a tutorial did and then marking every component as client-side, and picking plain React for a public site and then rebuilding server rendering by hand.

Side by side

DimensionReactNext.js
What it isA library for building user interfaces from components. It handles rendering, state and updates, and leaves URLs, data loading, server code and delivery to you.A framework built on React. The same components, plus routing, a rendering strategy per route, server-side code, data fetching conventions, bundling and a deployment model.
Who makes the architectural decisionsYour team. Router, data fetching, folder structure, form handling and build configuration are chosen, integrated and kept compatible by you.Mostly the framework. Folders map to routes, layouts nest by folder, and each component runs on the server or the client according to rules you work within rather than design.
Where the HTML is producedIn the browser. The server sends a near-empty HTML shell and a JavaScript bundle, and the content exists only once that bundle has downloaded and run.Chosen per route: at build time, regenerated in the background or on demand, on the server for each request, or in the browser where that suits the page.
Search engines and link previewsGoogle renders JavaScript, so pages can be indexed, but later and with more ways to fail. Most link preview scrapers and many other crawlers see only the empty shell.The strongest position in the React world. Crawlers and scrapers receive finished HTML, with per-route titles, descriptions and social tags generated through a typed metadata API.
First load on an ordinary phoneNothing meaningful appears until the bundle has downloaded, parsed and executed. Code splitting helps, and returning users with a cached bundle barely notice.Content is visible before the JavaScript arrives, slow sections can stream in after the rest, and Server Components send no JavaScript at all for the parts that are not interactive.
HostingA folder of static files. Any static host, CDN or storage bucket can serve it, there is no server process to run, and it behaves the same everywhere.A Node.js server, a container or a platform with Next.js support. Static export is available and drops every server feature. Vercel, which builds the framework, is its smoothest home.
What running it costsStorage and bandwidth for static files, which is usually a small line. Your API and database are billed separately, wherever they live.Static routes cost what static files cost. Every request rendered on the server is compute you pay for, and a route that has quietly become dynamic can change the bill noticeably.
Backend and server codeSeparate by design. The application calls an API you build or already run, in Node.js, PHP, Python, .NET or anything else, and anything secret stays on that side.Can live in the same codebase. Server Components read data directly, server actions handle mutations and route handlers expose endpoints. Many products still keep a separate backend as well.
Concepts the team must holdReact itself, plus the libraries you chose. Everything runs in one environment, the browser, so the mental model and the debugging both stay in one place.React, plus the server and client component boundary, several caching layers with different lifetimes, rendering modes and framework conventions. Strong React engineers still need ramp-up time.
Development loopVite starts quickly and hot reloading is close to instant. There are few moving parts, so when something breaks there are few places to look.Good and improving, with more going on underneath: separate server and client builds, routing, and caching that can behave differently in development and in production.
Lock-inLow at the framework level. You depend on your router and data libraries, each replaceable on its own, and the components run anywhere React runs.Real, and worth naming. Components are portable, but file routing, server actions, caching APIs and the rendering layer are Next.js APIs that another framework will not accept unchanged.
UpgradesSeveral smaller dependencies, each upgraded on its own schedule. More pieces to track, and each step tends to be modest.One large dependency on a fast release cadence. Major versions have changed caching defaults and introduced a new routing model, and codemods cover only part of the work.
Embedding and non-web targetsA natural fit. Widgets on third-party pages, micro-frontends, browser extensions, desktop shells and screens inside an existing server-rendered application.A poor fit. The framework expects to own the whole page and the request, which is exactly what an embedded component or a desktop shell does not give it.
HiringAny competent React engineer can work in it from the first day, which makes it the widest pool you can draw from.A large pool, though real App Router experience with Server Components and caching is narrower than general React experience. Ask about it specifically when you interview.

Choose React when

  • The application lives behind a login. Admin panels, internal tools, dashboards, back-office systems and customer portals are never crawled, their users come back daily with the bundle already cached, and the first-load argument for server rendering mostly falls away. A plain React application serves these people well, and adding a server layer means paying for compute and complexity that no user experiences.
  • You are building something that runs on other people’s pages. A booking widget, a chat panel, a pricing calculator or a micro-frontend inside a larger shell needs to mount into the element it was given and nothing more. A Vite library build produces a bundle that can be dropped in with a script tag. Next.js assumes it owns the entire page and the request, which is precisely the assumption an embedded component cannot make.
  • You already have a backend you are keeping. If a Laravel, Django, Rails, .NET or Java application already handles authentication, sessions and the API, a plain React front end talks to it directly. Putting Next.js in between adds a second server tier that has to understand the same sessions and permissions, and having two places for the same rule to live is how security gaps appear.
  • Your hosting has to be static or tightly controlled. Some environments will happily serve files from a CDN or a bucket and will put any new runtime through a long security review. On-premise and air-gapped deployments, and client operations teams who will only run static assets, all point the same way. A folder of HTML, JavaScript and CSS is one of the easiest artefacts in software to deploy, audit and roll back.
  • The target is not a normal website. Desktop applications built with Electron or Tauri, browser extensions, screens inside a mobile WebView and kiosk software have no server rendering step to benefit from. React runs comfortably in all of them. Next.js would contribute a build pipeline and a set of conventions, with nothing for its server half to do.
  • Nearly everything on screen is interactive. Design tools, rich editors, spreadsheets, mapping applications and live monitoring dashboards are client state from top to bottom. Server Components pay off when much of a page is content that does not change in response to the user, and in an application where every component needs state and event handlers, the framework’s main advantage has very little to work on.
  • The team is small, or new to React, and the product does not need what the framework adds. One runtime, one mental model and errors that always happen in the same place make a plain React codebase easier to learn, review and hand over. That is worth more than an architecture that anticipates requirements the product does not have.

Choose Next.js when

  • Public pages have to rank. Marketing sites, content and documentation, e-commerce category and product pages, and landing pages for paid campaigns all need finished HTML in the first response, with a correct title, description and canonical URL on every route. Next.js produces that by default, and a client-rendered application has to be persuaded into it.
  • Your links get shared and the preview matters. When someone pastes a product page into a chat or a social post, the scraper building the preview card reads the raw HTML and moves on, and most of them never run JavaScript. If those cards are part of how you win customers, the page has to be rendered before it leaves the server.
  • First-load speed on ordinary phones and patchy connections affects revenue. Sign-up funnels, checkout journeys and search landing pages lose people during a blank screen. Server rendering with streaming shows meaningful content while the interactive parts are still loading, and Server Components keep the code for static sections out of the bundle entirely.
  • You are building one of the SaaS products that pair a public site with an application, and you want both in one codebase. One design system, one authentication flow, one deployment and one set of conventions across the marketing pages, the documentation and the logged-in product is a real reduction in work, provided the team is comfortable with the framework’s server side.
  • You publish a large number of content pages that must be fast and current. Static generation serves them from cache, and on-demand revalidation lets a change in your CMS refresh only the affected pages without a full rebuild. Doing the same with a client-rendered application means building that machinery yourself, and it rarely comes out as well.
  • One team owns the interface and its data, and you want fewer layers. A Server Component can query the database or an internal service directly, and a server action can handle a form submission, which removes the API endpoints that existed only to feed your own front end. That means less code, fewer network hops, and types that flow from the query to the component without a hand-maintained contract in between.
  • Something has to happen on a server before the page is drawn. Checking a session before rendering, redirecting by role, personalising content, or calling a third-party API with a secret key that must never reach the browser all need server code. With plain React you need a backend for those regardless, and if you have no other reason to run one, the framework’s server side is a convenient home for them.
  • Your plain React application has started growing a framework of its own. A hand-built server rendering setup, a prerendering script for the marketing routes, a homemade convention for mapping files to routes and a data loading layer bolted onto the router are the signs. At that point you are maintaining a less tested version of something you could adopt, and moving to Next.js is usually cheaper than continuing.

What each one is, and what the real alternatives are

React’s scope is deliberately narrow. Components describe what the interface should look like for a given state, hooks let them hold state and respond to change, and the renderer works out what has to change on the page and changes it. React does ship APIs for rendering on the server and for Server Components, and that sometimes muddies the comparison further. Those APIs are building blocks, though. Wiring them into routing, data loading, bundling and a production server is a substantial piece of engineering, and that wiring is what a framework provides.

Next.js provides it with strong opinions. Folders in the app directory become routes, layouts wrap the routes beneath them and persist across navigation, and every component is a Server Component unless it opts into the browser with a directive at the top of the file. Each route can be generated at build time, regenerated in the background, rendered per request or left to the client. Around that sit loading and error states declared as files, server actions for mutations, route handlers for endpoints, a metadata API for titles and social tags, and built-in handling for images and fonts. None of it is magic. All of it is code you would otherwise choose, integrate and maintain yourself.

This is also why the honest alternatives to Next.js are other ways of using React. The lightest is a single-page application built with Vite, with a router such as React Router or TanStack Router and a server-state library such as TanStack Query. Further along are other React frameworks: React Router’s framework mode, into which Remix was merged, and newer entrants such as TanStack Start. For content-heavy sites with small islands of interactivity, Astro renders pages to static HTML and hydrates only the React components that need it. What you are deciding is how much framework you want, and Next.js sits at the fuller end of that range.

One clarification saves a lot of confusion in team discussions. Learning Next.js is mostly learning React, plus a smaller set of server and framework concepts on top. The components, hooks, props and state are identical. What differs is where code runs, when it runs, and what the framework keeps between runs. That last part is where most of the difficulty sits, and it gets its own section below.

SEO, link previews and where the HTML comes from

A client-rendered React application sends the browser an HTML document that is nearly empty: a root element, a script tag and perhaps a loading indicator. The content exists only after the JavaScript has downloaded, run and usually fetched data from an API. Every reader that does not execute JavaScript sees that empty document, and so does every reader that gives up before the work is finished.

Google does execute JavaScript. Its crawler fetches the page, queues it for rendering, renders it in a headless browser and indexes what it finds, and a well-built single-page application can rank. The risks are in the details. Rendering happens after crawling, sometimes considerably after. A failed API call, a blocked script, a timeout or content that only appears after a click can leave an indexed page with nothing useful on it. Titles and descriptions set by script after load are not treated consistently by every crawler that matters to you. And Google is the generous case. Link preview scrapers for chat and social platforms generally read the raw HTML and nothing else, and many other crawlers, including a number of those feeding AI answer engines, behave the same way.

Next.js removes that whole category of risk by sending finished HTML. The title, description, canonical URL and social tags are in the document, the content is in the document, and the reader does not have to run anything to see either. Server rendering does not make a page rank by itself. Content, internal links, performance and authority still decide that, and technical SEO is still a discipline of its own. What server rendering removes is the chance that a sound page is invisible because a crawler gave up on its JavaScript.

There is a middle path worth knowing about. A plain React application with a handful of public routes can prerender those routes to static HTML at build time, which covers a small marketing section without adopting a framework. It works well for a modest number of pages that change rarely. Past that point, or once pages depend on data that changes often, you are rebuilding static generation and incremental updates by hand, and the framework does that job better than most teams will.

Hosting, running cost and the Vercel question

A Vite build produces a folder of static files, and that folder is the whole deployment artefact. Any static host serves it, any CDN caches it, a storage bucket can hold it, and rolling back means pointing at the previous folder. There is no process to keep alive, no runtime to patch and no compute bill that grows with traffic beyond bandwidth. The server work, authentication and data, happens in a backend you run separately and would be running anyway.

Next.js offers more than one deployment shape, and the shape you choose decides what you get. A static export produces plain files much like a Vite build, and in exchange drops everything that needs a server: per-request rendering, incremental regeneration, server actions, request interception and the default image optimisation. The full framework runs as a Node.js server, which self-hosts well in a container on a virtual machine or a container platform. It can also run on platforms that map routes onto serverless functions and edge caches, where the operations work is lighter and the bill follows usage.

Vercel builds Next.js and runs the platform it works most smoothly on. New capabilities tend to arrive there first, and the features that involve caching, image optimisation and incremental regeneration need no configuration. Self-hosting is entirely viable, and some of it needs deliberate work: incremental regeneration across several instances needs a shared cache rather than each instance’s own disk, image optimisation consumes compute somewhere and should be budgeted, and a few features take more setup outside Vercel. Other hosting platforms support the framework too, and community projects such as OpenNext target several more. None of this is a reason to avoid Next.js. It is a reason to decide where it will run before the first route is written, rather than discovering the dependency on the day you want to move.

On cost, compare mechanisms rather than anybody’s current price list. Static output costs storage and bandwidth. Server rendering costs compute per request, whether you pay for it as a server you size yourself or as usage on a platform that bills for requests, execution time and data transferred. The detail that catches teams out is that a Next.js route can become dynamic because of a single call that reads a cookie or a header, at which point every visit is rendered on the server. Pricing models on every platform change, so check the current rate card, and read the build output on each release, because it states which routes are static and which are rendered per request.

Complexity, the server and client boundary, and team skills

The complexity in a plain React application is spread across decisions. Which router, which data fetching approach, how forms are handled, where shared state lives, how the folders are organised. Each decision is simple on its own, and together they define the architecture, which is why two plain React codebases can look nothing like each other. A team with a senior engineer who makes those choices once and writes them down does well. A team that leaves them open tends to make them several times, inconsistently, in the same codebase.

The complexity in Next.js is concentrated in fewer, harder ideas. Server Components cannot hold state or run effects. Anything that needs the browser sits behind a client boundary, and props crossing that boundary have to be serialisable, so you cannot hand a function from a server component to a client one the way you would between ordinary components. Libraries that assume a window object exists must be isolated. Then there is caching: memoised requests, cached data, cached routes and a client-side router cache, each with its own lifetime and invalidation rules, and defaults that have shifted between major versions. A large share of the hard Next.js bugs turn out to be caching behaving exactly as configured, which was not how anybody believed it was configured.

There is a characteristic failure that shows whether a team has the appetite for this. When boundary errors start to bite, it is tempting to mark components as client components until the errors stop. Follow that far enough and you have a client-rendered application with a server attached, more JavaScript than the plain React version would ship, and a hosting bill for rendering that nobody benefits from. If the team is not going to engage with the server half of the framework, plain React is the more honest choice.

On skills, any React engineer is productive in a Vite application from the first day. Next.js asks for some server thinking as well: HTTP caching, sessions and cookies, where secrets may and may not appear, what runs at build time and what runs per request, and how the runtime behaves under load. TypeScript helps on both sides and helps Next.js more, because types can follow data from a server query into a component without a separately maintained API contract in between. For handover, the framework’s conventions are a genuine asset: a Next.js codebase is recognisable to someone who did not write it, while a plain React codebase is only as legible as the decisions its authors documented.

Lock-in, upgrades and how long the decision lasts

Next.js has changed direction before, and it is reasonable to plan for it doing so again. The move from the Pages Router to the App Router was a rethink of routing, data fetching and rendering rather than an incremental update, and caching defaults have changed between major releases. The Pages Router is still supported and plenty of production applications run on it, but new capability is concentrated in the App Router. The release cadence is fast, upgrades deserve a planned slot with testing, and codemods handle the mechanical edits without covering changes in behaviour.

Lock-in follows the same layers. Your components are React and will run in any React environment. Your routing structure, layouts, server actions, caching calls, metadata exports and request handling are Next.js APIs, and moving a substantial App Router application to another framework means rewriting all of that. The framework itself is open source under the MIT licence, so what binds you is the framework and its conventions, not a vendor contract, and the hosting pull described above is a separate, smaller commitment that you control by deciding early where the application runs.

Plain React has a different maintenance profile. React itself changes its public API slowly and announces removals well in advance, and recent additions such as the compiler that handles memoisation automatically have been opt-in. The maintenance burden sits in the libraries you assembled around it, each on its own release schedule and occasionally abandoned by its maintainers. You get more upgrades, each usually smaller, and you can replace any one piece without touching the rest. Create React App is the cautionary example on this side: projects that depended on it now sit on an unmaintained build tool, and the fix is a move to Vite that is usually straightforward but still has to be scheduled and done.

Either way, this decision is cheaper to reverse than most framework decisions, because the component layer survives. That should lower the stakes of choosing. It should not encourage choosing casually, because the routing, data and deployment layers are where the rewriting happens, and those are the layers that differ.

What moving between them actually involves

This is one of the friendlier migrations in front-end work, because both sides are React. Components, hooks, styling and most client-side logic move across unchanged. What changes is routing, data loading, environment configuration and the deployment pipeline, and those can be moved gradually rather than in one release.

Moving a plain React application to Next.js usually starts by running the existing application inside Next.js as it is: a single catch-all route renders the whole single-page application on the client, so the product behaves as it did before, now running on the new framework. From there, routes move to file-based routing one at a time, public pages move to server rendering or static generation first because that is where the benefit is, and client-side data fetching is replaced with server-side loading where it pays for itself. Expect friction from libraries that assume a browser, environment variables that need renaming because the two tools expose them to the browser differently, and authentication that has to be understood on the server for the first time.

Moving the other way happens too, usually when a team realises it adopted Next.js for an application that never needed a server: everything is a client component, nothing is public, and the server exists only to be operated. Leaving means choosing a router, replacing file-based routing with route definitions, moving any server actions and route handlers into a proper API, and replacing the framework’s handling of images, fonts and metadata. It is contained work, and for an application with no public pages it can reduce hosting cost and operational load considerably.

Before migrating in either direction, check whether the complaint is really about the framework. A slow Next.js application is more often a caching or dynamic rendering problem than a framework problem, and a profile will tell you which. A plain React product with poor search visibility frequently needs only its public pages split out into a separately rendered marketing site, while the application stays as it is. Both of those fixes cost a fraction of a migration and solve the problem people actually have.

Technologies involved

How we help

Common questions

Is Next.js better than React?

The question compares two things that are not alternatives. Next.js is built on React, so every Next.js application is written in React components. The real choice is between React on its own, usually built with Vite as a single-page application, and React inside Next.js, which adds routing, server rendering, static generation, server-side code and a deployment model. Next.js is the better fit for public sites that need search visibility and fast first loads. Plain React is the better fit for logged-in tools, embedded widgets and front ends to an existing backend, where a server layer adds cost without benefit.

Do I need to learn React before Next.js?

Yes, and trying to learn both at once makes each one harder. Next.js assumes you already understand components, props, state, hooks and effects, because every page you write is made of them. On top of that it adds server concepts: which components run on the server, what may cross into the browser, and what gets cached and for how long. When an error appears, someone learning both cannot tell whether React or the framework produced it. A few weeks building a plain React application first makes Next.js far easier to learn properly.

Can a plain React app rank on Google?

It can, because Google renders JavaScript before indexing, but the route is less reliable. Rendering happens after crawling, sometimes well after it, and a failed API call, a blocked script or content that only appears after a click can leave an indexed page empty. Link preview scrapers and many other crawlers do not run JavaScript at all. For an application behind a login none of this matters. For public pages that bring in customers, server-rendered or prerendered HTML is the safer foundation, whether that comes from Next.js or from prerendering a handful of routes.

Do we have to host Next.js on Vercel?

No. Next.js is open source under the MIT licence and runs anywhere Node.js runs, including a container on your own infrastructure, and a static export can go on any static host. Vercel builds the framework and offers the smoothest experience, with caching and image optimisation working without configuration. Self-hosting needs some deliberate work, notably a shared cache when several instances use incremental regeneration, and a plan for where image optimisation runs. Decide where the application will live before you build it, so that any dependency on one platform is a choice rather than a discovery.

Is Next.js overkill for a small project?

Size is the wrong measure. What matters is whether the project uses what the framework adds. A small public site that needs good search visibility uses it well, and could equally be served by a static export or a simpler static site generator. A small internal tool behind a login does not use it at all, and a plain React build with Vite will be quicker to set up, cheaper to host and easier to hand over. Next.js becomes overkill when its server half sits idle while the team still pays for its learning curve and its operation.

Does Next.js replace our backend?

For some products it can. Server Components can read from a database, server actions handle form submissions and route handlers expose endpoints, which covers the needs of many web applications. A separate backend is still the better design when several clients use the same API, such as a mobile app alongside the website, when you run heavy background jobs or long-running processes, or when the backend team works in another language. In those cases Next.js usually serves and shapes data for the web interface, while the core business logic lives elsewhere.

Is Create React App still a sensible way to start?

No. The React team deprecated Create React App in early 2025, and it no longer gets the maintenance a build tool needs. Existing applications still run, but they sit on an ageing toolchain. New plain React projects are usually started with Vite, which offers a faster development server and simpler configuration, and the React documentation also describes framework-based starting points. Moving an existing Create React App project to Vite is normally a contained job covering configuration, environment variable names, the HTML entry point and a few imports.

What about other React frameworks, such as React Router or TanStack Start?

They sit in the same category as Next.js: frameworks that add routing, data loading and server rendering on top of React. React Router’s framework mode, into which Remix was merged, leans on web standards and a straightforward data loading model, and TanStack Start is a newer option built around type-safe routing. Each is a legitimate choice with its own hosting and learning profile. Next.js remains the most widely adopted, which counts for hiring, documentation and third-party support. The first decision is still whether you need a framework at all, and which one comes after that.

Weighing React against Next.js?

Tell us the volumes, the compliance position and who would run it day to day. We will tell you which one we would choose for your case, and say so plainly when it is not the one we sell.

  1. 01A senior engineer reads it. Not a form queue, and not an account manager.
  2. 02We reply either with questions or with a straight answer that we are not the right fit.
  3. 03If it looks like a fit, a technical call with the person who would actually run the delivery.
  4. 04Then scope, effort and risk in writing, before anyone signs anything.

Two fields required. We reply to real enquiries. No list, no sequence.