Back to article index
TRANSMISSION / MINT6 min read

Architectural Subtraction in Nuxt 4

A content site does not need complex frontend state. Let routing, server rendering, and content queries each return to what they do best.

#Nuxt#Vue#Architecture
Complex modules gradually converging into a clear and compact Nuxt application core
Using architectural subtraction to create clearer boundaries

Many Nuxt projects become complicated not because the business is genuinely complex, but because we put every possible future into the architecture too early.

A personal blog is a useful calibration tool: it has a limited number of pages, a stable content structure, and interaction concentrated in a few areas. It reminds us that a framework’s most valuable contribution is often not adding capabilities, but removing unnecessary choices.

Begin with SSR by default

Technical articles need to be understood by search engines, and their body text should appear as early as possible on a slow connection. Nuxt’s default server-side rendering matches that goal.

export default defineNuxtConfig({
  modules: ['@nuxt/content'],
  nitro: {
    preset: 'cloudflare',
  },
})

There is no need to turn the whole site into an SPA or invent a separate loading strategy for every page. The page performs its first query and render on the server, and the client continues with the same router after taking over.

If one small area truly depends on browser APIs, use ClientOnly locally. Do not sacrifice the semantics and first screen of an entire page for one client component.

Keep content queries at the page boundary

The homepage, article index, and article detail page need different data shapes. Queries should stay close to the pages that consume them:

const { data: posts } = await useAsyncData('latest-posts', () => {
  return queryCollection('posts')
    .where('draft', '=', false)
    .order('publishedAt', 'DESC')
    .limit(6)
    .all()
})

This has several direct benefits:

  • The data participates in SSR instead of flashing into view after onMounted.
  • The query key is explicit, so Nuxt can reuse the payload during hydration.
  • The page knows what it needs without going through a generic store.
  • Draft filtering and sorting rules appear where they are actually relevant.

Only extract a composable or store when several pages truly share the same complex business state.

Extract only stable repetition into components

“It might be reusable” is not a component boundary. Stable repetition usually includes structure, semantics, and behavior at the same time.

On this blog, an article card is a good component. It appears on both the homepage and the index, with consistent metadata, title, summary, tags, and navigation behavior.

The homepage hero, however, does not necessarily need to become five small components just because it is large. It appears once, and its internal elements work together as one visual narrative. Splitting it too early only spreads styling relationships across files.

A component is not a file-organization tool. It is a UI contract that can be understood and reused independently.

Keep server APIs narrow

Authentication and comments are runtime data, so they belong in server/api. Each endpoint does one job:

POST /api/auth/register
POST /api/auth/login
POST /api/auth/logout
GET  /api/auth/session
GET  /api/comments/:post
POST /api/comments/:post

Narrow interfaces make authorization rules easier to review. Reading comments is public, creating one requires a session, and logging out destroys only the current token without touching the user profile.

If an endpoint handles reads, writes, login, and format conversion at the same time, it has already lost its boundary.

Put state back in the right place

Vue’s reactive state is convenient, but not every state belongs in JavaScript:

StateBest location
Current articleRoute
Search termPage ref
Tag filterURL query + page state
Signed-in userSSR-safe useState
Article bodyNuxt Content
Session truthServer-side D1

When state lives in the right place, a great deal of synchronization logic disappears naturally. We no longer watch routes to repair a store, and we no longer use localStorage to pretend that authentication is trustworthy.

Subtraction does not mean fewer features

Good architectural subtraction does not take anything away from the user. It preserves the complexity budget for the parts people can actually feel:

  • Clearer typography.
  • A faster first screen.
  • More reliable authentication.
  • More comfortable mobile reading.
  • A Markdown writing workflow that is easier to sustain.

Nuxt 4 provides more than enough capability. A mature project is not one that uses all of it, but one that knows which capabilities it can leave unused for now.

End of signal
READER CHANNEL

Comments

00
No comments yet. Be the first to respond.