Why I Choose Next.js for Every Project

7 min read
nextjsreactframeworksfrontendarchitecture

I have shipped twelve projects with Next.js. Enterprise sites for Din Tai Fung and Burlington at Planetary. Indie products like the Helsky Labs Dashboard and this very blog. Not one of them is dead. Not one has been rewritten in another framework.

Meanwhile, my feed is full of "goodbye Next.js" posts. Developers who used it for one project, hit a rough edge, and declared the framework finished. The discourse has that particular internet energy where everyone has strong opinions and nobody is shipping.

Here is my strong opinion: Next.js is still the best default for full-stack web apps in 2026. Not the best for everything. The best default. There is an important difference.

The Criticism Is Real. So What.

The complaints are not invented:

  • Breaking changes between major versions that make upgrading feel like a second job
  • Vercel lock-in that gets tighter with every release
  • App Router complexity that makes simple things feel over-engineered
  • Server Components mental model that trips up developers who have been writing React for years

I have hit every single one of these. The Planetary team lost a full sprint upgrading a client site from Pages Router to App Router. I have cursed at cryptic Server Component errors at 11 PM on a Tuesday. The criticism is earned.

But here is what the "goodbye" posts never mention: the alternatives have their own friction. They just have newer, less-documented friction. Trading a known set of problems for an unknown set of problems is not progress — it is a lateral move with extra learning curve.

What Actually Matters: Decisions I Do Not Have to Make

Convention over configuration. That is the real feature, and nobody writes thinkpieces about it because it is boring.

With Next.js, I do not decide where routes live. I do not configure SSR versus SSG versus ISR. I do not set up API routes. I do not wire TypeScript. I do not optimize images. These are solved problems, handled by the framework, and every decision I skip is time I spend on the actual product.

// This file IS the route, the data fetch, and the component.
// No configuration. No wiring. No ceremony.
// app/products/[id]/page.tsx
export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await getProduct(params.id);
  return <ProductDetails product={product} />;
}

For a solo developer shipping indie products on evenings and weekends, conventions are not a limitation. They are a force multiplier. Every hour I do not spend configuring a bundler is an hour I spend building the thing.

Server Components Changed How I Think

I resisted Server Components. The mental model felt wrong — why would I want components that cannot use state or effects? What is the point of React without interactivity?

Then I built the Helsky Labs Dashboard with them and something clicked.

// Before: the useEffect-fetch-loading-spinner dance
'use client';
export function ProductList() {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('/api/products')
      .then(r => r.json())
      .then(setProducts)
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <Spinner />;
  return <List items={products} />;
}

// After: just... get the data
export async function ProductList() {
  const products = await db.products.findMany();
  return <List items={products} />;
}

The "after" version ships less JavaScript, has no loading state, queries the database directly, and is half the code. For content-heavy sites — which describes most of what I build — going back to the useEffect waterfall feels like going back to callbacks after discovering async/await.

Turbopack Is Finally Not a Joke

The Turbopack skepticism was justified for years. It was announced with fanfare, delivered with delays, and spent a long time being "almost ready."

In 2026, it is ready. Next.js 16 made it the default. Dev server starts in under 200ms. HMR is nearly instant. Production builds are 2-3x faster. These are not benchmarks I read — these are numbers from my actual projects.

When you are shipping on 12 hours a week of side project time, seconds matter. Not philosophically. Literally.

The Vercel Thing

Yes, I deploy to Vercel. Yes, it bothers me a little that the framework and the platform are the same company. Yes, I am aware this makes me part of the "lock-in" people warn about.

git push origin main
# App is live 60 seconds later. HTTPS. CDN. Preview deploys. Zero config.

I can deploy to Netlify, Railway, or self-host. I have done all three. But for indie projects where DevOps is not the product, Vercel's free tier removes an entire category of problems from my life. The lock-in concern is valid in theory and irrelevant in practice for apps that make less than the Vercel free tier limit.

If my indie product gets big enough that Vercel pricing becomes a problem, that is the best problem I could possibly have.

The Honest Comparison

TanStack Start

The most interesting alternative right now. Tanner Linsley's type-safe routing is genuinely better than Next.js routing. The explicit data loading patterns are cleaner. No "use server" mental overhead.

I still choose Next.js because TanStack Start shipped in 2024 and I have been burned by immature frameworks before. Not because it is bad — because I do not have the luxury of debugging framework issues on my side project time. When it matures, I will revisit. That is not a dismissal. That is patience.

Astro

Astro wins for content sites. Period. If I were building a marketing page with three interactive elements, Astro's island architecture and 3x faster builds make it the obvious choice.

But most of my projects need more than three interactive elements. And I do not want to context-switch between frameworks across projects. One set of patterns in my head. One set of gotchas I already know. That has value.

Vite + React

For internal dashboards and SPAs behind auth, Vite is the right call. No SSR overhead, no server components, just React doing what React does best. I use this stack at Planetary for client admin panels.

For anything public-facing that needs SEO, it is not enough.

Remix / React Router 7

I respect the Remix philosophy. The data loading patterns are excellent. But the framework's trajectory has been unclear since the React Router merge, and "wait and see" is my current position. I am not writing a goodbye post about it. I am just not starting new projects with it.

When I Skip Next.js

  • Mobile apps. React Native with Expo. Different paradigm entirely.
  • Simple static sites. Astro or plain HTML. Not everything needs a framework.
  • Pure SPAs. Figma-style apps where SSR adds overhead with zero benefit. Vite + React.
  • Teams that know Vue or Svelte. Use Nuxt or SvelteKit. Do not force a framework because of one developer's preference.

I shipped DropVox in Swift. TokenCentric in Electron. The Helsky Labs Dashboard in Next.js. Each tool was chosen for the job. Loyalty to a framework is a bad instinct.

My Standard Setup

npx create-next-app@latest my-project --typescript --tailwind --app

npm install @tanstack/react-query    # Server state
npm install zod                       # Runtime validation
npm install lucide-react              # Icons

Flat structure. app/ for routes, components/ for shared UI, lib/ for utilities. If the folder structure is complex, the project scope crept.

The Actual Reason

If I am being fully honest, the deepest reason I choose Next.js is not technical. It is practical. I know it.

I know which abstractions leak. I know the weird edge cases. I know that dynamic routes with generateStaticParams behave differently in dev and prod. I know that middleware runs on the edge and cannot use Node.js APIs. I know these things because I have shipped a dozen projects and hit the walls myself.

Switching frameworks means discovering a new set of walls. For client work with generous timelines, that is fine. For side projects where I have 12 hours a week and the goal is shipping — familiarity compounds faster than any technical advantage a new framework could offer.

That is not "Next.js is objectively best." It is "Next.js is best for me, right now, given what I know and what I am building."

The difference matters more than most framework debates acknowledge.