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.
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 4 | Next.js 15 | |
|---|---|---|
| Language | Vue 3 | React 19 |
| Reactivity | ref/reactive (push-based) | hooks (pull-based) |
| SEO utilities | Built-in composables | next/head / metadata API |
| Blog/CMS | @nuxt/content | @next/mdx, Contentlayer |
| Layouts | First-class | layout.tsx in App Router |
| Component ecosystem | Smaller but growing | Extensive |
| Vercel support | First-class | First-class |
| Bundle size | Smaller default | Slightly 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.