Why I Chose Nuxt Over Next.js for My Portfolio

After building production apps with both frameworks, here's what pushed me toward Nuxt — and when Next.js would have been the right call instead.

James Ross Jr.

James Ross Jr.

Full-Stack Developer & Systems Architect

The Decision

When I started building this portfolio, I had a real choice to make. I've shipped production code in both React/Next.js and Vue/Nuxt. There was no obvious default — both ecosystems are mature, both have excellent Vercel support, and both handle SSR well. So the decision came down to a few specific tradeoffs that are worth documenting.

This isn't a "Nuxt is better than Next.js" post. It's an honest record of why Nuxt was the right call for this project, and where I'd flip the decision.


What I Was Building

The portfolio is a horizontal-scroll single-page app with 7 sections, a blog powered by Markdown files, individual service pages, and portfolio case studies. SSR matters for SEO. The blog needs content prerendering. The codebase is just me, so DX matters more than team familiarity.

That context shapes everything below.


Why Nuxt Won

1. The Composition API Clicks Differently in Vue

I've written a lot of React hooks. They work. But useEffect, dependency arrays, stale closures, and the mental overhead of managing reactivity explicitly adds up. The Vue 3 Composition API — ref, computed, watch — is more declarative and less footgun-prone for the way I think.

This isn't a knock on React. It's a preference born from using both. When you're moving fast on a solo project, writing code that reads closer to your intent matters.

2. useHead and useSeoMeta Are First-Class

In Next.js, meta management means either the next/head component or the newer App Router metadata API. Both work, but they're framework-specific wrappers around a DOM concern.

Nuxt ships useHead and useSeoMeta as composables that work the same way everywhere — server, client, nested components. For a site where every page needs distinct title, description, OG tags, canonical, and JSON-LD, that consistency removes friction.

useHead({
  title: 'James Ross Jr. — Full-Stack Developer & Systems Architect',
  meta: [
    { name: 'description', content: '...' }
  ],
  script: [
    { type: 'application/ld+json', innerHTML: JSON.stringify(schema) }
  ]
})

3. @nuxt/content for the Blog

The blog is Markdown files in a content/blog/ directory. With @nuxt/content v3, I define a collection, query it with queryCollection(), and render it with <ContentRenderer>. The content is indexed into SQLite at build time — no runtime file I/O, no custom API route, proper SSR.

I briefly tried rolling a custom API route using gray-matter and marked. It worked for client-side, but crawlers saw 13 words per blog page because the fetch didn't resolve during prerendering. The content module solves this at the framework level.

4. The Nuxt Module Ecosystem

Modules for sitemap, robots, OG image generation, Google Fonts, and image optimization are one line in nuxt.config.ts and just work. That's not unique to Nuxt — Next.js has excellent packages too — but the unified nuxt.config.ts configuration model keeps everything in one place.

5. Layouts

Nuxt's layouts system let me build a horizontal.vue layout that wraps the whole single-page scroll experience, and then opt into a standard vertical layout for blog posts and service pages. No context providers, no layout wrappers in page components.

layouts/
  horizontal.vue  ← portfolio home
  default.vue     ← everything else

Where I'd Choose Next.js Instead

Team familiarity. React is more widely known. If you're hiring or collaborating, Next.js has a larger talent pool.

App Router ecosystem. For large-scale data-fetching patterns, React Server Components and the App Router's caching model are genuinely ahead of Nuxt's current offering.

Ecosystem depth. The React component ecosystem (Shadcn/UI, Radix, etc.) is deeper. Building a complex design system? React has more off-the-shelf primitives.

Personal familiarity. If you've been writing React for years and Vue feels foreign, don't switch frameworks mid-project. The productivity loss isn't worth the philosophical win.


The Actual Tradeoffs

Nuxt 4Next.js 15
LanguageVue 3React 19
Reactivityref/reactive (push-based)hooks (pull-based)
SEO utilitiesBuilt-in composablesnext/head / metadata API
Blog/CMS@nuxt/content@next/mdx, Contentlayer
LayoutsFirst-classlayout.tsx in App Router
Component ecosystemSmaller but growingExtensive
Vercel supportFirst-classFirst-class
Bundle sizeSmaller defaultSlightly larger

What I'd Do Differently

If I were starting today, I'd wire up queryCollection from the beginning instead of starting with a custom API route. The content.config.ts file is required and non-obvious — the docs bury this. I spent time debugging blank blog pages before realizing the collection wasn't defined.

Also: Nuxt Content v3 uses better-sqlite3 for its SQLite index. On Vercel, you need to rebuild the native module for the right architecture — pnpm rebuild better-sqlite3 before the build step. If you're deploying there, add that to your buildCommand in vercel.json before you find out the hard way.


The Bottom Line

I chose Nuxt because Vue's Composition API fits how I think, @nuxt/content handles the blog correctly, and the module ecosystem covers my SEO needs without custom infrastructure. For a solo project where I'm writing every line, that DX advantage compounded.

For a team project, a heavy design system requirement, or an existing React codebase, I'd be on Next.js without hesitation. Neither framework is the answer to every problem — knowing why you're picking one is the point.