React, on its own, is a client-side rendering library. Import it, call createRoot(...).render(<App />), and it will happily manage a tree of components inside a <div id="root"> — but everything else is left to you. No opinion on how a URL maps to a page. No opinion on whether HTML gets generated on a server or only ever in the browser. No opinion on how your code gets split into separate downloadable chunks instead of one enormous bundle. Those aren't oversights; React's own scope is deliberately narrow so it can be embedded into arbitrary environments — a browser extension, a native app via React Native, a single widget dropped into an existing page that isn't a React app at all.
The moment you're building a full application rather than a widget, though, you need answers to all three of those questions, and you need them to be consistent across every page:
/products/42 has to resolve to some component, and that mapping needs to be discoverable rather than hand-wired in a giant switch statement.<div> and build the whole page in JavaScript (slow first paint, bad for a crawler that doesn't execute scripts), or does the server send real HTML that's already visible before any JavaScript runs?Next.js answers all three, opinionatedly, out of the box: file-based routing, server rendering by default, and automatic code splitting per route. That's really the whole pitch. "Just use React" and "use Next.js" aren't competing answers to the same question — they're answers to two different questions. "How do I describe UI as a function of state?" is React's question. "How do I ship a real application with routing, fast initial loads, and reasonable bundle sizes without hand-building all of that myself?" is the one a framework like Next.js exists to answer. Reaching for plain React and reinventing a router, an SSR pipeline, and a bundler split strategy is a legitimate choice for a component library or a tiny widget — it's rarely the pragmatic one for an actual product with multiple pages and real users on real networks.