IMO as a frontend dev: It's a question of app architecture, optional hosting and middleware features, and whether your app needs any of that.

TLDR: Vite is good for client-side only single-page apps. Next can do that too (with more complexity), but its real strength lies in being able to handle some of the stuff a more dedicated backend traditionally would, offloading some of the frontend rendering and fetching to the backend server (or serverless funcs).

Next also has the additional benefit (big, IMO) of being the most popular way to do React (and arguably web frontends in general)... it has a thriving ecosystem of third-party packages, examples, and users. IMO that is a bigger benefit than any strict feature parity comparison. I don't think it's an exaggeration to say that Next is now the "standard" way to do React -- and when React last overhauled their docs, Next replaced the default CRA for a while, until they added some other frameworks in a subsequent update. It makes a big difference in day to day work, especially with other teams/companies, when you can share the same framework and mental model.

Longer version:

CRA, Vite, and Next.js all do what's typically expected of a framework these days: improve on the barebones React DX. They all handle bundling (via webpack or esbuild or Rollup or similar), tests (usually jest), hot module refresh (live previews as you edit), built-in support for SASS and TypeScript, etc. And they also give you a single framework to upgrade together, integrated and tested by a company, instead of a dozen individual packages across the React ecosystem -- drastically reducing the chance of incompatibilities. Now, these are all nice for the developer, but they don't really help the end-user UX.

Next goes a step beyond those basics: If your data lives in a backend and visitors see a frontend, Next also takes up the space in between the two, in a place where reverse proxies, Varnish caches, and other optimizations of that sort used to live. It lets you optimize the heck out of your pages (and components now, with the App Router 13+) so the server can do a lot of the work and send the client pre-built HTML blobs, reducing the fetching and rendering done on the client.

In a traditional clientside SPA (like create-react-app or default Vite), the user's browser downloads a bunch of JS, runs it, fetches any data it needs, and then combines that data and your code into a bunch of HTML. That all happens in real-time on the client, and it can sometimes be a bottleneck for users with slower devices or networks.

The power of Next.js is in letting you, the developer, more carefully manage that lifecycle. The downside is that it often forces you to manage that lifecycle when you don't want to -- like in a traditional SPA.

For example, let's say you run a website with a blog and an ecommerce section.

The blog is the easier example: The content lives somewhere (doesn't really matter where, say a headless CMS or a bunch of Markdown files). Next can do a serverside fetch/readfile, combine the content with your React components, and then render HTML + hydratable JS all on the server, efficiently packing it down and code-splitting it for delivery via CDN. When a user sees that page, what they first download is the HTML, so the blog content is immediately readable. Half a second later the JS is downloaded in chunks and rehydrated in the background. When the user clicks the "Next post" link, Next fetches ONLY THE JSON CONTENT from its middleware -- as distinct from your original backend -- and uses that small blob (probably just a few kB) and uses that to re-render the next post's blog body, while keeping the rest of the page (headers, menus, scripts, etc.) unchanged. This makes inter-page navigation lightning fast, because it's not refetching/reloading EITHER all the JS or the HTML, just the JSON of the content that changed. And for clients without JS, or who didn't get to load it in time before navigating, clicking "Next post" will simply fall back to the serverside rendered version of that page. It takes a second or two longer in that case because it has to download the entire HTML all over again, but it's a graceful degradation. At the same time, every time a user visits that blog post, Next will do a background staleness check. If the post's been updated, it will re-fetch and re-build just that one page, so subsequent visitors see the updated version (incremental static regeneration, ISR). This is all kinda magic, but well-documented in their docs.

The advantage of all this is faster end-user browsing. The disadvantage is increased complexity for the developer: you have to think through the mental model of this, separating your SPA into chunks of "what can I offload to the server" and "what must happen on the client" and then "how do I deal with invalidations".

A more complex example is ecommerce: Each product can be a component that's mostly rendered serverside, with the names, descriptions, etc. all coming from the DB. The images get fetched from S3 or Imgix or wherever, then next/image adds progressive rendering, blur thumbnails, and responsiveness to them. The pricing data component changes maybe once a day, and is statically built and invalidated not on a timer but a specific invalidation tag. The inventory level, however, is completely client-side (shows a React suspense while loading, nothing if JS is disabled)... that goes through a serverless func that pulls it from the latest DB tables/KV store/whatever.

All of that is out-of-the-box functionality for Next.js, and it can all live in one single codebase -- frontend and middleware and API routes and serverside and clientside stuff, just in different folders with file-based routing.

It can be really powerful, but it's also a lot of overhead and mental complexity if you just need a barebones clientside SPA (which Vite is great for). With great power comes great... need for debugging?

Now, a lot of these principles can be offloaded to a real backend too. You want to make serverside components and send them over the wire as HTML, cached on a CDN? You can do all that with standard backend software and keep the frontend light. I don't know what you've implemented in Python/FastAPI, but if it sounds like it's duplicating some of the features of Next, it probably is.

Next makes a lot of sense in an all-JS team, cuz it lets them handle the entire frontend, all the middleware, and some of the backend (minus the actual content) all in the same TypeScript codebase, no Python or Java or .NET or PHP required. (And Vercel is also moving into DBs as a service: https://vercel.com/guides/using-databases-with-vercel). If you have a separate backend written in some other language & framework, it may or may not overlap with Next features. That's why it's an architectural decision, not just a strict frontend comparison.

--------------------------------------------

Now, whereas the above was me trying to objectively analyze the differences... this is my opinion as a user: The App Router is annoying. I loved Next before 13, with the old pages router doing serverside stuff and useEffect() plus dynamic() loading in clientside stuff. It was a pretty intuitive mental model.

The App Router makes things a lot more complicated. Still, I had to learn it, alongside React Server Components and Suspense etc., just to keep up with the latest versions -- especially since I work in a support role now (for a headless CMS vendor) and deal with other companies using Next in various permutations, often with the App Router nowadays.

I understand the benefits it offers, but I question whether the incrementally better UX is worth the much higher complexity and worse DX. The old page router is still available, but it seems to me they're trying to phase it out in favor of the new shiny, which is a shame because it's so much more complex. Maybe I'm just getting old :)

My old argument for Next.js is that it abstracts away a lot of the chaos of the old React ecosystem (where nothing was provided out of the box, not even routing) into a single sleek package. To a large extent that is still true, but it's also true for Vite, Astro, Remix, etc. these days -- they all offer many similar benefits over vanilla React, without the mental complexity of Next + App Router.

But... I would still 100% choose Next for new projects. Like it or not, it is the industry standard (at least for now) and it has the best chance of both still being used and maintained in 5 years, and also allowing your project to grow into its features as needed.

The basic setup with Next, even with the App Router, only takes a day or two to learn and implement. If you use `create-next-app` it'll only take like five minutes to get a example website up and running, then a few hours to get it hooked up to a real backend using prototype frontend components.

But for many months after that, you'll hit subtle differences in, say, `use client` vs `use server` and how to embed one in the other, how to get around its built-in monkeypatching of `fetch()` to cache its results, how to properly implement ISR with sensible invalidations, etc. It's a long-tailed learning curve.

If it were me, that learning curve is worth it. Mostly because I know I can trust Next and Vercel to do their jobs once I learn them, and I'm not going to run into their limitations before I hit my own. And having a large ecosystem + userbase is huuuuuuuuuge for not having to reinvent the wheel, being able to find StackOverflow or ChatGPT solutions, etc.

But YMMV. Build your prototypes in a few frameworks and see which ones are the easiest to work with for your use case, and then imagine your app in 5 years with a bunch of new features, and try to imagine that it's like using that framework to build those.

Good luck!

edit: Heck, screw "5 years". Just imagine 2 years, lol. Nothing in Javascript lasts longer than that.