Learn Nuxt - Architecture & Modularization
Series/Learn Nuxt/Episode 18
Episode 18 of 24

Learn Nuxt - Architecture & Modularization

This episode covers scalable Nuxt project architecture: feature-based structure, module development and reusable composables, separating responsibilities between UI, data, and logic, and patterns for keeping the codebase maintainable as it grows.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Small applications are easy to maintain; large applications with messy structure become a nightmare. Episode 18 covers Nuxt architecture and modularization: how to organize code so it's easy to understand, test, and develop as a team.

The question is no longer "does the code work", but "will this code be easy to maintain in six months". This episode gives you the principles and patterns: feature-based structure, reusable composables, separation of responsibilities, and habits that keep a codebase from rotting.

Feature-Based Project Structure

Organizing by Feature

Instead of grouping files by type globally, group them by feature. Compare these two approaches:

Struktur per fitur
app/
  features/
    checkout/
      components/FormAlamat.vue
      composables/useCheckout.ts
      utils/hitungOngkir.ts
    produk/
      components/KartuProduk.vue
      composables/useProduk.ts

A feature-based structure puts everything related to checkout in one place. When a feature changes, you only touch one folder — not hunt through files in five different folders.

When to Use This Structure

A feature-based structure shines in medium-to-large applications with many domains. For small applications, the standard Nuxt structure is enough. Don't force it — adopt it when cross-cutting pain starts to be felt.

Module Development and Composable Utilities

Composables as Logic Units

A composable is a function that wraps stateful logic for reuse. It's the primary modularization unit in Nuxt:

JSComposable hitung keranjang
export function useKeranjangCount() {
  const keranjang = useKeranjangStore()
  const jumlah = computed(() => keranjang.items.length)
 
  return { jumlah }
}

useKeranjangCount() wraps the logic of counting cart contents. Because it's auto-imported, any component can call it without an import — and the logic stays in one place.

Rules for Good Composables

A few guidelines: names always start with use, one composable handles one responsibility, and return reactive values ready to use. Good composables keep components thin and logic easy to test.

Nuxt Modules

For code used across projects, build a Nuxt module. A module is a configurable package added to the modules list. This is an advanced step — start with internal composables first, then extract into a module when truly needed.

Separation of Concerns: UI, Data, Logic

Three Clear Layers

Separate the three things often mixed into a single component:

  • UI: template, styling, and pure interaction.
  • Data: fetching and storage, for example in composables or server routes.
  • Logic: business rules, validation, and calculations.

Components should display data and handle interaction, not contain business rules:

HTMLKomponen tanpa logika bisnis
<script setup lang="ts">
const { produk, pending } = await useProduk()
</script>
 
<template>
  <div v-if="pending">Memuat...</div>
  <div v-else>
    <KartuProduk v-for="item in produk" :key="item.id" v-bind="item" />
  </div>
</template>

The component above only connects the UI with data from useProduk(). Rules like discount calculations or address validation live in utils and the server — easy to test and replace.

The Benefits of Separation

With good separation: the UI can be replaced without touching logic, logic can be tested without a browser, and the team can work on different layers without big conflicts.

Keeping a Scalable Nuxt Codebase

Habits That Preserve Scalability

A few practices that prevent a codebase from rotting:

  • Consistent naming: one term for one concept across the whole codebase.
  • Active linting and typechecking: rules run automatically, not voluntarily.
  • Small components: components over a few hundred lines should be split.
  • Brief documentation: write down the reasoning behind non-trivial decisions.

Continuous Refactoring

Scalability isn't a one-time result, it's a habit. Set aside regular time for refactoring: remove dead code, split big components, and standardize patterns. A clean codebase makes new features much faster to build.

Conclusion

Episode 18 gives you the blueprint for a healthy codebase: feature-based structure for large scale, composables as reusable logic units, a firm separation between UI, data, and logic, and refactoring habits that keep code maintainable.

Key takeaways:

  • A feature-based structure places all code for one feature in one folder.
  • Composables wrap stateful logic so it's reusable and testable.
  • Separate UI, data, and logic so they're easy to replace and test.
  • Components should be thin — delegate logic to composables and utils.
  • Consistent naming and active linting prevent codebase rot.
  • Continuous refactoring is the key to long-term scalability.

In the next episode, episode 19, we will discuss modern tooling and build automation — using the Nuxt CLI and Vite effectively, strict TypeScript integration, CI/CD pipelines for Nuxt applications, and automated linting, formatting, and pre-commit hooks.

Learn Nuxt - Architecture & Modularization | Learn Nuxt