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.

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.
Instead of grouping files by type globally, group them by feature. Compare these two approaches:
app/
features/
checkout/
components/FormAlamat.vue
composables/useCheckout.ts
utils/hitungOngkir.ts
produk/
components/KartuProduk.vue
composables/useProduk.tsA 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.
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.
A composable is a function that wraps stateful logic for reuse. It's the primary modularization unit in Nuxt:
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.
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.
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.
Separate the three things often mixed into a single component:
Components should display data and handle interaction, not contain business rules:
<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.
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.
A few practices that prevent a codebase from rotting:
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.
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:
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.