Nuxt 4 di Tahun 2026: Struktur Direktori Baru dan Panduan Migrasi dari Nuxt 3

Panduan lengkap Nuxt 4 mencakup struktur direktori app/, singleton data fetching, shallow reactivity, serta langkah migrasi bertahap dari Nuxt 3 untuk developer Vue.js.

Nuxt 4 directory structure migration guide

Nuxt 4 hadir sebagai pembaruan major yang membawa perubahan arsitektural mendasar bagi ekosistem Vue.js. Sejak rilis awal pada Juli 2025 hingga versi 4.4 yang tersedia saat ini, framework ini telah menjalani proses pematangan yang signifikan. Struktur direktori didesain ulang dengan pendekatan yang lebih terorganisasi, data fetching layer dirombak menjadi singleton pattern, dan konteks TypeScript dipisahkan secara ketat antara client, server, serta shared code. Berbeda dengan transisi dramatis dari Nuxt 2 ke Nuxt 3 yang memerlukan penulisan ulang substansial, jalur migrasi kali ini dirancang dengan filosofi evolusioner -- perubahan dapat diadopsi secara bertahap tanpa mengganggu stabilitas aplikasi yang sudah berjalan di production.

Migrasi Otomatis dengan Codemod

Tim Nuxt telah menyediakan codemod resmi yang mengotomatisasi sebagian besar proses migrasi. Perintah npx codemod@latest nuxt/4/migration-recipe akan menangani restrukturisasi direktori, pembaruan pola data fetching, serta penggantian API yang sudah deprecated. Pendekatan ini meminimalkan kemungkinan human error selama proses migrasi berlangsung.

Struktur Direktori app/ yang Baru pada Nuxt 4

Perubahan paling fundamental pada Nuxt 4 terletak pada reorganisasi seluruh kode sumber aplikasi ke dalam direktori app/. Keputusan desain ini dilatarbelakangi oleh pertimbangan teknis yang konkret: file watcher pada Linux dan Windows mencatat peningkatan performa yang signifikan ketika kode aplikasi diisolasi dalam subdirektori tersendiri, terpisah dari node_modules/, .git/, dan berkas konfigurasi lainnya. Pemisahan ini mengurangi jumlah file yang perlu dipantau oleh sistem operasi secara drastis.

Berikut gambaran struktur direktori standar pada proyek Nuxt 4:

text
my-nuxt-app/
├─ app/
│  ├─ assets/
│  ├─ components/
│  ├─ composables/
│  ├─ layouts/
│  ├─ middleware/
│  ├─ pages/
│  ├─ plugins/
│  ├─ utils/
│  ├─ app.vue
│  ├─ app.config.ts
│  └─ error.vue
├─ content/
├─ public/
├─ shared/
├─ server/
└─ nuxt.config.ts

Hadirnya direktori shared/ merupakan tambahan yang layak mendapat sorotan khusus. Setiap composable, utility function, atau definisi tipe yang ditempatkan di dalam folder ini akan secara otomatis tersedia baik di sisi aplikasi Vue maupun di server Nitro. Mekanisme auto-import ini menghilangkan kebutuhan untuk menuliskan path import secara manual ketika sebuah validation schema atau helper function perlu digunakan di kedua konteks sekaligus.

Dari perspektif organisasi tim, pemisahan yang tegas antara app/ (kode client), server/ (kode backend), dan shared/ (kode lintas konteks) memberikan kejelasan yang sangat berharga. Developer baru yang bergabung ke dalam sebuah proyek dapat segera memahami batasan tanggung jawab masing-masing bagian tanpa perlu mempelajari dokumentasi internal yang panjang. Keuntungan ini semakin terasa pada proyek berskala enterprise dengan puluhan kontributor.

Langkah Demi Langkah Migrasi dari Nuxt 3 ke Nuxt 4

Proses migrasi dimulai dengan memperbarui package Nuxt ke versi terbaru. Framework ini dirancang untuk mendeteksi struktur flat (gaya Nuxt 3) secara otomatis dan tetap berjalan normal, sehingga tidak ada tekanan untuk memindahkan seluruh file sekaligus.

bash
npx nuxt upgrade --dedupe

Setelah dependensi diperbarui dan aplikasi berhasil di-build, tahap berikutnya adalah menjalankan codemod untuk memindahkan file-file aplikasi ke dalam direktori app/:

bash
npx codemod@latest nuxt/4/file-structure

Codemod ini secara otomatis memindahkan assets/, components/, composables/, layouts/, middleware/, pages/, plugins/, utils/, app.vue, error.vue, dan app.config.ts ke lokasi barunya di dalam app/. Sementara itu, file yang memang seharusnya berada di root proyek -- seperti nuxt.config.ts, server/, public/, dan content/ -- akan tetap berada di posisi semula.

Bagi tim yang belum siap melakukan restrukturisasi penuh karena alasan teknis tertentu (misalnya pipeline CI/CD yang memerlukan penyesuaian), Nuxt 4 menyediakan konfigurasi compatibility yang mempertahankan perilaku Nuxt 3:

nuxt.config.tstypescript
export default defineNuxtConfig({
  srcDir: '.',
  dir: { app: 'app' },
})

Konfigurasi di atas menginstruksikan Nuxt 4 untuk me-resolve file dari root proyek, sehingga struktur flat tetap berfungsi. Pendekatan ini memungkinkan migrasi dilakukan secara gradual -- misalnya, satu modul per sprint -- tanpa memaksakan perubahan besar dalam satu waktu.

Singleton Data Fetching Layer dan Reactive Keys

Salah satu perubahan terpenting pada Nuxt 4 adalah perombakan data fetching layer menjadi singleton pattern. Pada Nuxt 3, setiap pemanggilan useAsyncData atau useFetch dengan key yang sama di komponen berbeda menghasilkan salinan data yang independen. Nuxt 4 mengubah perilaku ini: komponen-komponen yang menggunakan key identik kini berbagi satu reactive reference tunggal.

app/composables/useProductData.tstypescript
export function useProductData(productId: string) {
  return useAsyncData(
    `product-${productId}`,
    () => $fetch(`/api/products/${productId}`),
    {
      getCachedData: (key, nuxtApp, ctx) => {
        if (ctx.cause === 'refresh:manual') return undefined
        return nuxtApp.payload.data[key]
      },
    },
  )
}

Perubahan ini membawa tiga konsekuensi penting yang perlu dipahami oleh setiap developer Vue.js:

Pertama, shared refs memastikan bahwa pemanggilan composable yang sama di komponen berbeda -- misalnya useProductData('abc') di header dan di sidebar -- akan mengembalikan referensi data, error, dan status yang identik. Pembaruan di satu titik secara otomatis tercermin di titik lainnya.

Kedua, automatic cleanup menangani pembersihan memori secara transparan. Ketika komponen terakhir yang mereferensikan sebuah key di-unmount, Nuxt akan membebaskan data terkait. Mekanisme ini mencegah memory leak yang kerap terjadi pada aplikasi single-page berdurasi panjang.

Ketiga, reactive keys memungkinkan penggunaan computed value atau ref sebagai bagian dari key. Perubahan pada nilai tersebut akan memicu refetching secara otomatis tanpa kode imperatif tambahan.

Callback getCachedData pada contoh di atas menerima objek context dengan properti cause. Nilai cause dapat berupa 'initial', 'refresh:hook', 'refresh:manual', atau 'watch', memberikan kontrol granular atas kapan data segar harus di-fetch versus kapan data dari cache sudah cukup memadai.

Perubahan Nilai Default dari null ke undefined

Nilai default data dan error dari useAsyncData serta useFetch berubah dari null menjadi undefined pada Nuxt 4. Seluruh pengecekan kondisional yang menggunakan === null perlu diperbarui menjadi === undefined, atau sebaiknya menggunakan loose equality check (== null) yang menangkap kedua nilai sekaligus.

Shallow Reactivity sebagai Default untuk Performa Lebih Baik

Nuxt 4 mengubah tipe ref yang digunakan oleh useAsyncData dan useFetch dari ref (deep reactive) menjadi shallowRef. Implikasi praktisnya adalah Vue tidak lagi melakukan tracking rekursif terhadap setiap properti nested dalam objek data. Untuk response API yang mengandung objek bersarang dalam atau array berukuran besar, perubahan ini menghasilkan peningkatan performa yang terukur karena overhead reactivity system berkurang secara signifikan.

app/pages/dashboard.vuetypescript
<script setup lang="ts">
const { data: metrics } = await useFetch('/api/dashboard/metrics')

// Direct property mutation won't trigger reactivity
// metrics.value.visits = 100  // Won't trigger re-render

// Replace the entire value to trigger updates
metrics.value = { ...metrics.value, visits: 100 }

// Or opt into deep reactivity for this specific call
const { data: settings } = await useFetch('/api/settings', {
  deep: true,
})
</script>

Pada sebagian besar skenario -- halaman dashboard, daftar produk, detail artikel, atau timeline aktivitas -- data ditampilkan tanpa mutasi langsung pada properti nested. Untuk kasus-kasus tersebut, shallow reactivity bekerja dengan sempurna tanpa memerlukan perubahan kode sedikitpun. Opsi deep: true tetap tersedia untuk situasi spesifik seperti form editing atau komponen interaktif yang memodifikasi properti bagian dalam secara langsung.

Filosofi di balik perubahan ini sejalan dengan prinsip yang semakin diadopsi oleh framework modern: memberikan default yang optimal untuk mayoritas kasus penggunaan, sambil tetap menyediakan escape hatch bagi skenario yang membutuhkan perilaku berbeda.

Siap menguasai wawancara Vue.js / Nuxt.js Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Pemisahan Konteks TypeScript dan Peningkatan Type Safety

Nuxt 4 menghasilkan empat konfigurasi TypeScript terpisah, masing-masing untuk konteks yang berbeda:

  • .nuxt/tsconfig.app.json -- konteks aplikasi Vue (komponen, pages, composables)
  • .nuxt/tsconfig.server.json -- konteks server Nitro (API routes, server middleware)
  • .nuxt/tsconfig.shared.json -- konteks shared (utility lintas konteks)
  • .nuxt/tsconfig.node.json -- konteks build-time (konfigurasi, scripts)

Pemisahan ini menghasilkan pengalaman development yang jauh lebih baik. IDE tidak lagi menyarankan API server (misalnya defineEventHandler) ketika developer sedang menulis kode komponen Vue, dan sebaliknya. Autocompletion menjadi lebih akurat, dan kesalahan import lintas konteks yang sebelumnya lolos dari pengecekan statis kini terdeteksi sejak dini.

File tsconfig.json di root proyek bertindak sebagai project reference yang menghubungkan keempat konfigurasi tersebut:

json
{
  "files": [],
  "references": [
    { "path": "./.nuxt/tsconfig.app.json" },
    { "path": "./.nuxt/tsconfig.server.json" },
    { "path": "./.nuxt/tsconfig.shared.json" },
    { "path": "./.nuxt/tsconfig.node.json" }
  ]
}

Perubahan ini berdampak pada cara type checking dijalankan di pipeline CI/CD. Perintah vue-tsc kini memerlukan flag -b (build mode) agar project references diproses dengan benar:

bash
# Before (Nuxt 3)
nuxt prepare && vue-tsc --noEmit

# After (Nuxt 4)
nuxt prepare && vue-tsc -b --noEmit

Perlu dicatat pula bahwa compilerOptions.noUncheckedIndexedAccess kini diaktifkan secara default. Akses elemen array atau properti objek melalui index sekarang mengembalikan T | undefined, bukan T. Perubahan ini membantu menangkap potensi error runtime -- seperti mengakses array[5] pada array yang hanya memiliki tiga elemen -- pada saat compile time.

Normalisasi Nama Komponen dan Integrasi Vue Router v5

Nuxt 4.3 melakukan upgrade ke Vue Router v5, menghapus dependensi pada unplugin-vue-router yang sebelumnya digunakan untuk file-based routing. Bagi sebagian besar aplikasi, perpindahan ini bersifat transparan dan tidak memerlukan modifikasi kode.

Aspek yang lebih berdampak adalah normalisasi penamaan komponen. Nuxt 4 menerapkan konvensi penamaan yang konsisten berdasarkan lokasi file di dalam direktori components/. Sebuah file yang terletak di components/dashboard/MetricsCard.vue akan secara otomatis mendapatkan nama DashboardMetricsCard di seluruh konteks -- termasuk <KeepAlive>, Vue DevTools, stack trace error, dan test utilities.

vue
<!-- app/pages/dashboard.vue -->
<template>
  <NuxtPage :keepalive="{
    include: ['DashboardMetricsCard', 'DashboardRecentActivity'],
  }" />
</template>

Proyek yang menggunakan <KeepAlive> dengan filter include atau exclude berdasarkan nama komponen perlu memverifikasi dan memperbarui string nama yang digunakan. Perilaku lama, di mana nama komponen bisa bervariasi tergantung konteks pemanggilan, tidak lagi berlaku. Standardisasi ini menyederhanakan proses debugging dan memudahkan komunikasi antar anggota tim ketika membahas komponen tertentu.

Menangani Breaking Changes pada Head Management

Nuxt 4 mengintegrasikan Unhead v2, yang membawa breaking changes pada API useHead dan useSeoMeta. Properti yang sudah di-deprecate sejak lama -- seperti vmid, hid, children, dan body -- telah dihapus sepenuhnya. Penggunaan reactive getter (fungsi tanpa argumen) kini menjadi pola yang direkomendasikan untuk meta tag yang bergantung pada data dinamis:

app/pages/product/[id].vuetypescript
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)

useSeoMeta({
  title: () => product.value?.name ?? 'Product',
  ogTitle: () => product.value?.name ?? 'Product',
  description: () => product.value?.description ?? '',
  ogImage: () => product.value?.imageUrl ?? '',
})
</script>

Fitur template parameters (%s di dalam title template) dan alias sorting yang sebelumnya tersedia secara bawaan kini perlu diinstal sebagai plugin eksplisit apabila masih dibutuhkan:

app/plugins/unhead.tstypescript
import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'

export default defineNuxtPlugin({
  setup() {
    const unhead = injectHead()
    unhead.use(TemplateParamsPlugin)
    unhead.use(AliasSortingPlugin)
  },
})
Batas Waktu Dukungan Nuxt 3

Nuxt 3 akan terus menerima security patches dan perbaikan bug kritis hingga 31 Juli 2026. Setelah tanggal tersebut, Nuxt 3 secara resmi memasuki status end-of-life dan tidak lagi didukung. Merencanakan serta mengeksekusi migrasi sebelum tenggat waktu tersebut sangat penting untuk memastikan aplikasi production tidak berjalan pada versi framework yang sudah tidak dipelihara.

Checklist Migrasi dan Kesalahan Umum yang Perlu Diwaspadai

Meskipun codemod otomatis menangani mayoritas perubahan struktural, terdapat sejumlah item yang memerlukan perhatian manual dari developer:

  • Penghapusan window.__NUXT__: Objek global ini dihapus setelah proses hydration pada Nuxt 4. Seluruh akses langsung perlu diganti dengan useNuxtApp().payload yang merupakan API resmi untuk mengakses payload data.
  • Hook pages:extend digantikan oleh pages:resolved: Hook baru ini dieksekusi setelah proses scanning page meta selesai, memberikan akses ke informasi halaman yang lebih lengkap dan akurat.
  • Perubahan parameter dedupe: Nilai boolean diganti dengan string deskriptif. refresh({ dedupe: true }) menjadi refresh({ dedupe: 'cancel' }), sementara false menjadi 'defer'. Perubahan ini memberikan kejelasan semantik tentang perilaku yang diinginkan.
  • Inline styles terbatas pada komponen Vue: CSS global kini dimuat sebagai file terpisah secara default. Tambahkan features: { inlineStyles: true } pada konfigurasi untuk mengembalikan perilaku Nuxt 3 jika diperlukan.
  • clearNuxtState melakukan reset ke initial value: Perilaku lama yang mereset ke undefined dapat dipertahankan dengan clearNuxtState('key', { reset: false }).

Berikut urutan migrasi yang direkomendasikan untuk aplikasi production:

  1. Jalankan npx nuxt upgrade --dedupe dan pastikan aplikasi berhasil di-build tanpa error
  2. Eksekusi codemod lengkap: npx codemod@latest nuxt/4/migration-recipe
  3. Restrukturisasi file ke dalam app/ (ditangani oleh file-structure codemod)
  4. Perbarui seluruh pengecekan === null menjadi === undefined pada logika data fetching
  5. Verifikasi konfigurasi <KeepAlive> dengan nama komponen yang sudah dinormalisasi
  6. Sesuaikan pipeline CI untuk menggunakan vue-tsc -b --noEmit
  7. Jalankan full test suite dan tangani error TypeScript baru yang muncul dari noUncheckedIndexedAccess

Untuk memperdalam pemahaman tentang konsep-konsep Nuxt yang sering diujikan dalam proses rekrutmen, SharpSkill menyediakan materi pertanyaan wawancara Vue/Nuxt yang mencakup topik-topik kunci dari Nuxt 3 hingga Nuxt 4. Selain itu, panduan SSR dan static generation memberikan fondasi pemahaman tentang strategi rendering yang tetap relevan pada versi terbaru.

Kesimpulan

Migrasi dari Nuxt 3 ke Nuxt 4 merupakan langkah strategis yang membawa perbaikan arsitektural dan peningkatan developer experience secara menyeluruh. Berikut rangkuman poin-poin kunci yang perlu dikuasai:

  • Nuxt 4.4 telah menstabilkan struktur direktori app/, singleton data fetching layer, dan pemisahan konteks TypeScript sebagai fitur yang siap digunakan di lingkungan production
  • Perintah npx codemod@latest nuxt/4/migration-recipe mengotomatisasi restrukturisasi direktori, penggantian API deprecated, dan pembaruan pola data fetching dalam satu langkah
  • Shallow reactivity melalui shallowRef meningkatkan performa secara default untuk halaman yang dominan menampilkan data, tanpa memerlukan perubahan kode pada sebagian besar kasus
  • Konfigurasi TypeScript terpisah per konteks (app, server, shared, node) mengeliminasi kebocoran tipe lintas konteks dan menghasilkan autocompletion IDE yang lebih presisi
  • Dukungan Nuxt 3 berakhir pada 31 Juli 2026 -- migrasi sebelum tanggal tersebut memastikan aplikasi tetap menerima security patches dan perbaikan bug
  • Vue Router v5 dan Unhead v2 membawa API yang lebih bersih namun menghapus sejumlah properti deprecated yang perlu diaudit secara menyeluruh selama proses migrasi

Penguasaan terhadap perubahan-perubahan ini bukan hanya relevan untuk kebutuhan teknis sehari-hari, melainkan juga merupakan materi yang kerap diujikan dalam interview untuk posisi Vue.js developer. Pemahaman mendalam tentang singleton data fetching, TypeScript context splitting, dan strategi migrasi bertahap menunjukkan tingkat kompetensi yang diapresiasi oleh tim rekrutmen di industri.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Tag

#nuxt
#vue
#migration
#typescript
#tutorial

Bagikan

Artikel terkait