Stop Defaulting to Next.js: A Senior Engineer's Guide to
Technology
Stop Defaulting to Next.js: A Senior Engineer's Guide to the React Ecosystem
Choosing between React and Next.js isn't about which is better. It's about matching your project's architecture to its actual requirements without over-engineering.
Neviox Digital· Updated
Share this article
A side-by-side comparison of React and Next.js
You stare at your terminal. You need to scaffold a new frontend project. Five years ago, this was a solved problem. You typed your scaffolding command, went to grab a coffee, and came back to a working development environment ready for your components.
Today, the landscape is fractured. The official React documentation explicitly points you away from plain React and nudges you toward full-stack frameworks, with Next.js sitting heavily at the top of the list. If you ask Twitter or read the latest tech blogs, you'll be told that Single Page Applications (SPAs) are dead and Server-Side Rendering (SSR) is the only way forward.
Get Expert Insights Weekly
Subscribe to our newsletter and be the first to learn about the latest innovations and expert insights from the world of technology.
I sit in architecture meetings every week where teams are paralyzed by this exact choice. They read an article saying Next.js is a bloated, over-engineered mess, and then immediately read another saying vanilla React is a legacy approach unfit for modern web development.
The truth is, both extremes are wrong.
After migrating dozens of codebases—both from React to Next.js, and surprisingly often, from Next.js back to plain React—I've noticed a distinct pattern. The choice between React and Next.js isn't about which technology is objectively "better." It is about what you are actually building, your team's operational maturity, and how much complexity you are willing to tolerate.
Let's cut through the noise and talk about how to actually make this architectural decision.
The Core Distinction: The Engine vs. The Assembly Line
Before we argue about which to use, we need to establish exactly what we are comparing. Comparing React to Next.js is fundamentally flawed. It is like comparing a high-performance engine to an entire factory assembly line.
React is an unopinionated view library. It does one thing exceptionally well: it takes your data and turns it into DOM elements. That's it. It doesn't care how you handle routing. It doesn't care how you fetch data. It doesn't care how you manage global state or bundle your assets. When you build a plain React app (these days, usually via Vite), you are signing up to make all of those decisions yourself. You will choose React Router or TanStack Router. You will choose Redux, Zustand, or Context. You are building a custom vehicle from scratch.
Next.js, on the other hand, is an aggressively opinionated full-stack framework built on top of React. It makes the decisions for you. It provides a file-system-based router. It dictates how you fetch data. It blurs the line between the client and the server. It handles asset optimization, code splitting, and server-side rendering out of the box.
If React is the engine, Next.js is the entire car, the dealership, and the mechanic.
This sounds great on paper. Why wouldn't you want all those decisions made for you? Because opinions come with trade-offs, and frameworks come with a tax.
The Next.js Tax: React Server Components and the Caching Labyrinth
If you haven't used Next.js since the introduction of the App Router and React Server Components (RSCs), you are in for a brutal mental model shift.
In a traditional React SPA, your mental model is simple: everything runs in the user's browser. You fetch data, you update state, the UI re-renders.
Next.js fundamentally breaks this model. By default, components in the Next.js App Router are Server Components. They render entirely on the server. They cannot use `useState`, they cannot use `useEffect`, and they cannot listen to browser events like `onClick`. If you need interactivity, you have to explicitly opt-in by adding the `"use client"` directive at the top of your file.
This forces you to constantly think about the boundary between your server and your client. You end up playing a game of component tetris, trying to push your `"use client"` directives as far down the component tree as possible to maximize server-side rendering benefits.
But the real tax of Next.js is the caching.
Next.js aggressively caches everything it can to improve performance. At one point, it had four overlapping caching mechanisms: the Request Memoization, the Data Cache, the Full Route Cache, and the Router Cache.
Look, I have spent hours—sometimes days—debugging why a Next.js route was returning stale data to a user, only to realize I misunderstood how the Router Cache interacts with a server mutation. When you adopt Next.js, you are no longer just a frontend developer. You are a distributed systems engineer managing cache invalidation across server and client boundaries.
If your application doesn't strictly need this level of server-side optimization, you are taking on a massive amount of cognitive load for zero tangible benefit.
Real World Scenario 1: The B2B Financial Dashboard (When Vite Wins)
Let's look at a practical example where choosing Next.js is a costly mistake.
Imagine you are building an internal B2B financial dashboard for day traders. This application sits entirely behind a strict `/login` route. It requires connecting to WebSockets to stream real-time stock prices. It features massive, complex data grids and highly interactive charting libraries that allow users to draw trend lines.
Does this app need SEO? Absolutely not. Google's web crawlers will never see it because it requires authentication.
Does this app benefit from Server-Side Rendering? No. The data changes every millisecond. Rendering an initial HTML shell on the server with stock prices that will be immediately outdated the moment they hit the browser is a waste of server CPU cycles.
In this scenario, plain React (scaffolded with Vite) is king.
You want a Single Page Application. You want a heavy client-side bundle that downloads once and then relies entirely on client-side state and WebSocket connections to update the UI.
Furthermore, the deployment story for a Vite SPA is vastly superior for this use case. A built Vite app is just a collection of static HTML, CSS, and JavaScript files. You can drop those files into an AWS S3 bucket, put CloudFront in front of it, and host an application that scales infinitely for pennies a month. You don't need a Node.js server running. You don't need to worry about serverless function cold starts.
By forcing Next.js into this architecture, you gain nothing and you inherit the complexity of managing server boundaries and deployment overhead.
Real World Scenario 2: The Real Estate Marketplace (When Next.js Wins)
Now, let's flip the script.
Imagine you are building a public-facing real estate marketplace—think Zillow or Redfin. You have millions of property listing pages.
If you build this as a plain React SPA, you are effectively committing business suicide.
When a web crawler from Google or Facebook hits a React SPA, it gets a nearly empty HTML file with a `<div id="root"></div>` and a massive JavaScript bundle. While Google's crawlers can execute JavaScript, it is slower, less reliable, and actively hurts your indexing speed.
More importantly, when a user shares a property link on iMessage or Twitter, you need a dynamic Open Graph image (the preview card) showing the specific house, the price, and the location. A plain React SPA cannot generate dynamic meta tags per route before the JavaScript executes. Everyone who shares your link will just see your generic company logo.
This is where Next.js is non-negotiable.
Next.js allows you to use Server-Side Rendering (SSR) or Incremental Static Regeneration (ISR) to pre-render the HTML for every single property listing on the server. When Google crawls the page, it sees fully formed HTML with all the text, images, and links immediately available. When a user shares a link, the server intercepts the request and injects the correct dynamic meta tags for that specific property.
Additionally, the perceived performance for the end user is drastically better. They see the fully rendered property page instantly, while React "hydrates" in the background to make the buttons interactive. For e-commerce, marketing sites, and public catalogs, Next.js isn't just a nice-to-have; it is a fundamental business requirement.
The Deployment and Vendor Lock-in Reality
We cannot talk about Next.js without talking about hosting.
Next.js is created and maintained by Vercel. Vercel is an excellent hosting platform, but their primary business model is getting you to host your Next.js applications on their infrastructure.
While Next.js is open source and you can technically host it anywhere, the reality is that deploying a modern Next.js App Router application outside of Vercel is significantly more painful than it should be. Features like Image Optimization, Middleware, and ISR are deeply optimized for Vercel's edge network.
If you try to containerize a Next.js app via Docker and run it on your own AWS ECS cluster, you will quickly find yourself writing custom cache handlers and fighting with memory leaks. It is absolutely doable—many large enterprises do it—but it requires a dedicated DevOps effort.
Plain React via Vite has zero vendor lock-in. Because it outputs static files, you can host it on Netlify, Cloudflare Pages, AWS S3, GitHub Pages, or a $5 DigitalOcean droplet with Nginx. It simply does not matter.
If your company has strict compliance requirements that dictate hosting everything on-premise or within specific AWS GovCloud regions, you need to factor the Next.js deployment tax into your architectural decision.
The Practical Takeaway: Your Decision Matrix
Stop treating Next.js as the default for every React project. Frameworks are tools, and applying the wrong tool to a problem will slow your team down and introduce unnecessary bugs.
If you are starting a project tomorrow, use this strict decision matrix:
Choose Plain React (Vite) if:
1. Your application is entirely behind an authentication wall (internal tools, B2B dashboards, SaaS admin panels).
2. SEO is completely irrelevant to your product's success.
3. Your app relies heavily on complex client-side state, WebSockets, or WebGL (like a browser-based photo editor or a Figma clone).
4. You want ultra-cheap, static hosting without maintaining a Node server.
Choose Next.js if:
1. You are building a public-facing site where SEO is critical (e-commerce, blogs, marketing sites, public directories).
2. You need dynamic Open Graph tags for social sharing.
3. Initial page load speed is tied directly to your conversion rates.
4. You have a mixed-content application where some pages are static and others need to be dynamically rendered on the server.
Architecture is about making intentional trade-offs. Don't adopt a framework just because the documentation tells you to. Understand the problem you are solving, evaluate the complexity you are taking on, and build exactly what you need—nothing more, nothing less.
Neviox Digital is a forward-thinking agency at the intersection of innovation and community. With a strong focus on inspiring tech solutions, we are passionate about empowering businesses to navigate the digital landscape. Our work extends beyond creating websites and apps! We build connections, drive digital transformation, and foster collaboration. Our mission is to prioritize the power of technology to spark positive change, deliver measurable results, and shape a better future for communities around the world.
Do you have a vision for a digital solution? Want to share your technical expertise or promote your brand? Let’s collaborate and build the future together!