1. Pengenalan Nuxt.js
Nuxt.js adalah framework full-stack berbasis Vue.js yang menyediakan arsitektur terstruktur untuk membangun aplikasi web modern. Dikembangkan oleh tim Nuxt Labs, framework ini menghilangkan konfigurasi manual yang rumit dan menyediakan fitur-fitur bawaan seperti SSR, SSG, auto-imports, dan routing otomatis.
Nuxt 3 (versi stabil saat ini) dibangun di atas Vue 3, Vite, dan Nitro (server engine), menjadikannya salah satu framework full-stack paling lengkap di ekosistem JavaScript.
Mengapa Memilih Nuxt.js?
| Keunggulan | Penjelasan |
|---|---|
| Zero Configuration | Routing, bundling, dan optimasi bekerja out-of-the-box tanpa setup manual |
| SSR & SSG | Dukungan server-side rendering dan static generation dalam satu framework |
| Auto-Imports | Komponen, composables, dan utils di-import otomatis tanpa deklarasi manual |
| Server Engine (Nitro) | Server API, middleware server, dan deploy ke berbagai platform cloud |
| SEO-Friendly | Meta tags, sitemap, dan prerendering built-in untuk SEO optimal |
| Ekosistem Modules | Ratusan modul siap pakai untuk auth, CMS, analytics, UI, dan lainnya |
Nuxt.js vs Alternatif Lain
| Aspek | Nuxt.js | Next.js | Astro |
|---|---|---|---|
| Base Framework | Vue.js | React | Multi (Vue, React, Svelte) |
| SSR | β Ya (Nitro) | β Ya | β Partial |
| SSG | β Ya | β Ya | β Ya (utama) |
| File-based Routing | β Otomatis | β Otomatis | β Otomatis |
| Server API | β Nitro | β API Routes | β Endpoint |
| Learning Curve | π’ Mudah | π‘ Sedang | π’ Mudah |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β NUXT APPLICATION β β β β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β Vue 3 + Composition API β β β β βββββββββββ ββββββββββββ βββββββββββββββββββ β β β β β Pages β βComponentsβ β Composables β β β β β β(routing)β β(reusable)β β(useFetch, useStateβ β β β β ββββββ¬βββββ ββββββββββββ βββββββββββββββββββ β β β βββββββββΌββββββββββββββββββββββββββββββββββββββββββββ β β β β β βββββββββΌββββββββββββββββββββββββββββββββββββββββββββ β β β Nuxt Core (Vite + Nitro) β β β β ββββββββββββ βββββββββββββ βββββββββββββββββ β β β β β Vite β β Nitro β β Nuxt Modules β β β β β β (bundler)β β (server) β β (plugins) β β β β β ββββββββββββ βββββββββββββ βββββββββββββββββ β β β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β β Output: SSR (server) β SSG (static) β CSR (SPA) β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2. Instalasi dan Setup
Untuk membuat proyek Nuxt baru, Anda bisa menggunakan beberapa package manager. Nuxt menggunakan nuxi sebagai CLI resminya.
Membuat Proyek Baru
# Membuat proyek Nuxt baru dengan npx npx nuxi@latest init nuxt-app # Atau dengan pnpm pnpm dlx nuxi@latest init nuxt-app # Masuk ke direktori cd nuxt-app # Install dependencies npm install # Jalankan development server npm run dev # Output: # Nuxt 3.x.x with Nitro # β Local: http://localhost:3000/
Struktur Proyek Nuxt
nuxt-app/ βββ .nuxt/ β Generated (auto) βββ assets/ β CSS, fonts, images (processed) βββ components/ β Vue components (auto-imported) β βββ AppHeader.vue β βββ AppFooter.vue β βββ UserCard.vue βββ composables/ β Composables (auto-imported) β βββ useAuth.js βββ layouts/ β Layout components β βββ default.vue βββ middleware/ β Route middleware β βββ auth.js βββ pages/ β File-based routing β βββ index.vue β / β βββ about.vue β /about β βββ blog/ β βββ index.vue β /blog β βββ [id].vue β /blog/:id βββ plugins/ β Vue plugins βββ public/ β Static files (tidak diproses) β βββ favicon.ico βββ server/ β Server-side code β βββ api/ β β βββ hello.js β /api/hello β βββ middleware/ β βββ utils/ βββ app.vue β Root component βββ nuxt.config.ts β Konfigurasi Nuxt βββ package.json βββ tsconfig.json
Konfigurasi Dasar
// nuxt.config.ts
export default defineNuxtConfig({
// Meta tags global untuk SEO
app: {
head: {
title: 'BeebaneLabs - Tutorial Web Development',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ name: 'description', content: 'Tutorial web development modern' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
}
},
// CSS global
css: ['~/assets/css/main.css'],
// Modul yang digunakan
modules: [
'@pinia/nuxt',
'@nuxtjs/tailwindcss',
'@nuxt/image'
],
// Konfigurasi development
devtools: { enabled: true }
})
File nuxt.config.ts mendukung TypeScript secara native. Gunakan defineNuxtConfig() untuk mendapatkan type hints dan autocompletion yang akurat di editor Anda.
3. Routing Otomatis
Salah satu fitur terbaik Nuxt adalah file-based routing. Setiap file .vue di folder pages/ secara otomatis menjadi route. Tidak perlu konfigurasi router manual seperti di Vue Router standalone.
Struktur Routing
pages/
βββ index.vue β /
βββ about.vue β /about
βββ contact.vue β /contact
βββ blog/
β βββ index.vue β /blog
β βββ [slug].vue β /blog/:slug (dynamic)
β βββ [...slug].vue β /blog/* (catch-all)
βββ user/
β βββ [id].vue β /user/:id
β βββ [id]/
β βββ index.vue β /user/:id
β βββ posts.vue β /user/:id/posts
β βββ settings.vue β /user/:id/settings
βββ products/
βββ [[category]].vue β /products atau /products/:category
(opsional parameter)
Dynamic Routes dengan Parameter
<template>
<div class="blog-post">
<h1>{{ post.title }}</h1>
<p class="meta">Ditulis oleh {{ post.author }} Β· {{ post.date }}</p>
<div class="content" v-html="post.content" />
</div>
</template>
<script setup>
// useRoute() untuk mengakses parameter URL
const route = useRoute()
const slug = route.params.slug
// useFetch() β composable bawaan Nuxt untuk data fetching
const { data: post, error } = await useFetch(`/api/posts/${slug}`)
// Jika post tidak ditemukan, tampilkan error 404
if (error.value) {
throw createError({
statusCode: 404,
statusMessage: 'Artikel tidak ditemukan'
})
}
// Dynamic meta tags untuk SEO
useHead({
title: post.value?.title,
meta: [
{ name: 'description', content: post.value?.excerpt }
]
})
</script>
Navigasi dengan NuxtLink
<template>
<nav>
<!-- NuxtLink menggantikan <a> tag, memberikan SPA navigation -->
<NuxtLink to="/">Beranda</NuxtLink>
<NuxtLink to="/blog">Blog</NuxtLink>
<NuxtLink to="/about">Tentang</NuxtLink>
<!-- Dynamic link -->
<NuxtLink :to="`/blog/${post.slug}`">
{{ post.title }}
</NuxtLink>
<!-- External link -->
<NuxtLink to="https://github.com" external>
GitHub
</NuxtLink>
<!-- Prefetching (default aktif) -->
<NuxtLink to="/products" no-prefetch>
Produk (tanpa prefetch)
</NuxtLink>
</nav>
</template>
Nested Routes dengan NuxtPage
<template>
<div class="user-profile">
<h1>Profil: {{ user.name }}</h1>
<nav class="user-tabs">
<NuxtLink :to="`/user/${user.id}`">Overview</NuxtLink>
<NuxtLink :to="`/user/${user.id}/posts`">Postingan</NuxtLink>
<NuxtLink :to="`/user/${user.id}/settings`">Pengaturan</NuxtLink>
</nav>
<!-- NuxtPage merender child routes di sini -->
<NuxtPage />
</div>
</template>
<script setup>
const route = useRoute()
const { data: user } = await useFetch(`/api/users/${route.params.id}`)
</script>
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β NUXT FILE-BASED ROUTING β β β β pages/about.vue βββββββ /about β β pages/blog/index.vue βββ /blog β β pages/blog/[id].vue ββββ /blog/123 (dynamic) β β pages/user/[id]/ β β β βββ index.vue ββββββββ /user/42 β β βββ posts.vue ββββββββ /user/42/posts β β β β Nuxt otomatis generate Vue Router config β β dari struktur folder pages/ β β β β pages/ β .nuxt/router.js (generated) β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
4. Server-Side Rendering (SSR)
SSR adalah mode default Nuxt. Setiap request halaman di-render di server terlebih dahulu, lalu dikirim ke browser sebagai HTML lengkap. Ini sangat penting untuk SEO dan performa initial load.
Cara Kerja SSR di Nuxt
1. Browser mengirim request β GET /blog/my-post 2. Server (Nitro) menerima request 3. Nuxt menjalankan lifecycle: a. Middleware server (server/middleware/) b. Data fetching (useFetch, useAsyncData) c. Render komponen Vue menjadi HTML string 4. HTML + data dikirim ke browser 5. Vue "menghidupkan" (hydrate) HTML di browser 6. Setelah hydration, aplikasi berjalan sebagai SPA Keuntungan: - Search engine bisa meng-crawl konten lengkap - First Contentful Paint (FCP) lebih cepat - Meta tags ter-render di server
Data Fetching di SSR
// useFetch β composable bawaan Nuxt untuk SSR-safe fetching
// Otomatis: dijalankan di server saat SSR, di client saat navigasi SPA
// Contoh 1: useFetch sederhana
const { data: products, pending, error } = await useFetch('/api/products', {
query: { category: 'electronics', limit: 10 },
// Cache key β data di-cache berdasarkan key unik
key: 'products-electronics',
// Transform data sebelum digunakan
transform: (response) => response.items,
// Re-fetch setiap 60 detik
getCachedData: (key, nuxtApp) => {
return nuxtApp.payload.data[key] || null
}
})
// Contoh 2: useAsyncData untuk logic kompleks
const { data: userProfile } = await useAsyncData('user-profile', async () => {
// Bisa melakukan multiple fetch atau logic kompleks
const [user, posts, stats] = await Promise.all([
$fetch('/api/users/me'),
$fetch('/api/users/me/posts'),
$fetch('/api/users/me/stats')
])
return { user, posts, stats }
}, {
// Hanya jalankan di server (tidak di client)
server: true,
// Lazy: jangan block rendering, fetch di background
lazy: false
})
// Contoh 3: Lazy fetching (tidak block rendering)
const { data: comments } = await useLazyFetch(`/api/posts/${id}/comments`)
// UI langsung dirender, comments muncul setelah fetch selesai
Hati-hati dengan akses window, document, atau localStorage di komponen yang di-SSR. Objek ini tidak tersedia di server. Gunakan onMounted() atau if (process.client) untuk mengakses browser API.
<script setup>
// β Salah β akan error di server
const width = window.innerWidth
// β
Benar β gunakan onMounted
onMounted(() => {
const width = window.innerWidth
console.log('Window width:', width)
})
// β
Atau gunakan useNuxtApp().$isServer
const nuxtApp = useNuxtApp()
if (!nuxtApp.isServer) {
const token = localStorage.getItem('token')
}
// β
Composable yang aman untuk SSR
const isClient = ref(false)
onMounted(() => { isClient.value = true })
</script>
5. Static Site Generation (SSG)
SSG memungkinkan Anda meng-generate halaman statis HTML saat build time. Ini cocok untuk blog, dokumentasi, dan landing page yang kontennya jarang berubah. Hasilnya bisa di-host di CDN manapun tanpa perlu server Node.js.
Mengaktifkan SSG
// nuxt.config.ts β konfigurasi untuk SSG
export default defineNuxtConfig({
// Route prerendering β generate halaman statis
nitro: {
prerender: {
routes: [
'/',
'/about',
'/blog',
'/pricing'
]
}
},
// Atau set seluruh app sebagai static
// Jalankan: npx nuxi generate
// Ini akan meng-generate semua halaman sebagai HTML statis
})
// Jalankan perintah untuk generate static
// npm run generate
// Hasil: .output/public/ β siap di-deploy ke CDN
Dynamic Routes dengan SSG
<script setup>
// Untuk dynamic routes, Nuxt perlu tahu semua slug yang tersedia
// agar bisa generate halaman statis untuk setiap slug
// Method 1: Define routes via nuxt.config.ts
// nitro.prerender.routes: ['/blog/post-1', '/blog/post-2']
// Method 2: Gunakan sitemap module
// @nuxtjs/sitemap bisa auto-generate routes dari API
// Fetch data β saat SSG, ini dijalankan di build time
const route = useRoute()
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`, {
key: `post-${route.params.slug}`
})
// Data di-embed ke HTML statis
// Saat user mengunjungi halaman, data langsung tersedia tanpa fetch ulang
</script>
Hybrid Rendering
// Nuxt mendukung hybrid rendering!
// Beberapa halaman SSR, beberapa SSG, beberapa SPA
export default defineNuxtConfig({
routeRules: {
// Halaman beranda β SSR (selalu fresh)
'/': { prerender: true },
// Blog β SSG (diprerender saat build)
'/blog/**': { prerender: true },
// Dashboard β SPA (client-side only, perlu auth)
'/dashboard/**': { ssr: false },
// API β ISR (revalidate setiap 60 detik)
'/api/products/**': { swr: 60 },
// Halaman statis β cache selamanya
'/about': { static: true },
// Redirect
'/old-page': { redirect: '/new-page' }
}
})
Hybrid rendering adalah fitur powerful Nuxt yang memungkinkan Anda memilih mode rendering per-route. Ini berarti Anda bisa memiliki blog yang di-SSG, dashboard yang SPA, dan halaman produk yang SSR β semua dalam satu aplikasi!
6. Auto-Imports
Nuxt secara otomatis meng-import komponen, composables, dan helper functions. Anda tidak perlu menulis import statement secara manual β cukup gunakan langsung di template atau script.
Apa yang Di-auto-import?
1. KOMPONEN (dari folder components/)
File: components/UserCard.vue
β Langsung pakai: <UserCard />
β Nested: components/blog/PostCard.vue β <BlogPostCard />
2. COMPOSABLES (dari folder composables/)
File: composables/useAuth.js
β Langsung pakai: const { user, login } = useAuth()
3. UTILS (dari folder utils/)
File: utils/format.js β export function formatRupiah() {...}
β Langsung pakai: formatRupiah(150000)
4. VUE APIs (built-in)
ref(), computed(), watch(), onMounted(), etc.
β Tidak perlu: import { ref } from 'vue'
5. NUXT APIs (built-in)
useFetch(), useRoute(), useState(), navigateTo(), etc.
β Tidak perlu: import { useFetch } from '#app'
Komponen Auto-Imported
<template>
<div class="user-card">
<img :src="user.avatar" :alt="user.name" />
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<span :class="['badge', user.role]">{{ user.role }}</span>
</div>
</template>
<script setup>
// Props dengan defineProps β juga auto-imported
const props = defineProps({
user: {
type: Object,
required: true
}
})
</script>
<style scoped>
.user-card {
padding: 1.5rem;
border-radius: 12px;
background: var(--card-bg);
border: 1px solid var(--border-color);
}
.badge {
padding: 4px 12px;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 600;
}
.badge.admin { background: #ef4444; color: white; }
.badge.member { background: #3b82f6; color: white; }
</style>
<template>
<div class="users-page">
<h1>Daftar Pengguna</h1>
<!-- Tidak perlu import UserCard β langsung pakai! -->
<div class="users-grid">
<UserCard
v-for="user in users"
:key="user.id"
:user="user"
/>
</div>
</div>
</template>
<script setup>
// Tidak perlu import ref, useFetch β auto-imported!
const { data: users } = await useFetch('/api/users')
</script>
Lazy Components
<template>
<div>
<!-- Komponen berat yang jarang dilihat -->
<!-- Prefix LazyLoad untuk lazy loading -->
<LazyHeavyChart v-if="showChart" :data="chartData" />
<!-- Komponen modal yang jarang muncul -->
<LazyUserModal v-model="showModal" />
<!-- Komponen di bawah fold -->
<LazyBlogComments :post-id="postId" />
</div>
</template>
<script setup>
const showChart = ref(false)
const showModal = ref(false)
const postId = useRoute().params.id
</script>
7. Composables dan Helpers
Composables adalah fungsi yang menggunakan Vue Composition API untuk menyimpan dan berbagi stateful logic. Nuxt membuat composables sangat mudah digunakan berkat auto-imports.
Membuat Composable
// composables/useCounter.js
// Auto-imported β langsung pakai useCounter() di mana saja
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
function increment() { count.value++ }
function decrement() { count.value-- }
function reset() { count.value = initialValue }
// Computed
const isPositive = computed(() => count.value > 0)
const isZero = computed(() => count.value === 0)
return {
count: readonly(count), // readonly agar tidak bisa diubah dari luar
increment,
decrement,
reset,
isPositive,
isZero
}
}
Composable dengan Server State
// composables/useAuth.js
export function useAuth() {
// useState β shared state yang SSR-safe
// Key harus unik untuk menghindari conflict
const user = useState('auth-user', () => null)
const token = useState('auth-token', () => null)
const isAuthenticated = computed(() => !!user.value)
const isAdmin = computed(() => user.value?.role === 'admin')
async function login(email, password) {
try {
const response = await $fetch('/api/auth/login', {
method: 'POST',
body: { email, password }
})
user.value = response.user
token.value = response.token
// Simpan token di cookie (SSR-safe)
const cookieToken = useCookie('auth_token', {
maxAge: 60 * 60 * 24 * 7, // 7 hari
httpOnly: false,
secure: true,
sameSite: 'lax'
})
cookieToken.value = response.token
return { success: true }
} catch (error) {
return { success: false, error: error.data?.message }
}
}
async function logout() {
user.value = null
token.value = null
// Hapus cookie
const cookieToken = useCookie('auth_token')
cookieToken.value = null
await navigateTo('/login')
}
async function fetchUser() {
if (!token.value) return
try {
const data = await $fetch('/api/auth/me', {
headers: { Authorization: `Bearer ${token.value}` }
})
user.value = data.user
} catch {
await logout()
}
}
return {
user: readonly(user),
isAuthenticated,
isAdmin,
login,
logout,
fetchUser
}
}
Built-in Nuxt Composables
| Composable | Kegunaan |
|---|---|
useFetch() | Data fetching SSR-safe dengan caching |
useAsyncData() | Data fetching kompleks dengan custom logic |
useRoute() | Akses informasi route saat ini |
useRouter() | Programmatic navigation |
useState() | SSR-safe reactive state (shared) |
useHead() | Dynamic head/meta tags |
useCookie() | SSR-safe cookie access |
useError() | Akses error yang sedang aktif |
useRequestHeaders() | Akses headers dari incoming request |
useRuntimeConfig() | Akses runtime configuration |
8. Server API Routes
Nuxt memiliki Nitro server engine yang memungkinkan Anda membuat API endpoints langsung di dalam proyek Nuxt. File di folder server/api/ secara otomatis menjadi API routes.
Membuat API Route
// server/api/hello.js
// Otomatis tersedia di: GET /api/hello
export default defineEventHandler((event) => {
return {
message: 'Halo dari Nuxt Server API!',
timestamp: new Date().toISOString()
}
})
API dengan Query dan Body
// server/api/products.js
// GET /api/products?category=electronics&limit=10
export default defineEventHandler(async (event) => {
// Mengambil query parameters
const query = getQuery(event)
const { category, limit = 20, page = 1 } = query
// Fetch dari database atau external API
const products = await db.products.findMany({
where: category ? { category } : {},
take: parseInt(limit),
skip: (parseInt(page) - 1) * parseInt(limit),
orderBy: { createdAt: 'desc' }
})
const total = await db.products.count({
where: category ? { category } : {}
})
return {
products,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
totalPages: Math.ceil(total / parseInt(limit))
}
}
})
// server/api/products.post.js
// Suffix .post = hanya menerima POST request
export default defineEventHandler(async (event) => {
// Validasi body request
const body = await readBody(event)
if (!body.name || !body.price) {
throw createError({
statusCode: 400,
statusMessage: 'Name dan price wajib diisi'
})
}
// Simpan ke database
const product = await db.products.create({
data: {
name: body.name,
price: parseFloat(body.price),
category: body.category || 'uncategorized',
description: body.description || ''
}
})
return { success: true, product }
})
Server Middleware
// server/middleware/auth.js
// Middleware ini berjalan untuk SEMUA server routes
export default defineEventHandler((event) => {
// Hanya untuk API routes yang dimulai dengan /api/protected
const path = getRequestURL(event).pathname
if (path.startsWith('/api/protected')) {
const token = getHeader(event, 'Authorization')
if (!token) {
throw createError({
statusCode: 401,
statusMessage: 'Token tidak ditemukan'
})
}
// Verifikasi token dan attach user ke event context
try {
const user = verifyToken(token.replace('Bearer ', ''))
event.context.user = user
} catch {
throw createError({
statusCode: 401,
statusMessage: 'Token tidak valid'
})
}
}
})
Server Utils
// server/utils/db.js
// Auto-imported di semua server files
import { PrismaClient } from '@prisma/client'
// Singleton pattern β satu instance Prisma
let prisma
export function usePrisma() {
if (!prisma) {
prisma = new PrismaClient()
}
return prisma
}
// Contoh penggunaan di server API:
// server/api/users/[id].js
// const db = usePrisma()
// const user = await db.user.findUnique({ where: { id: params.id } })
9. Nuxt Modules
Nuxt Modules adalah plugin yang memperluas fungsionalitas Nuxt. Ada ratusan modul komunitas yang bisa Anda gunakan, mulai dari UI framework hingga CMS dan analytics.
Menggunakan Modules
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
// UI & Styling
'@nuxtjs/tailwindcss',
'@nuxtjs/color-mode',
'@nuxtjs/google-fonts',
// State Management
'@pinia/nuxt',
// Images & Media
'@nuxt/image',
// SEO & Analytics
'@nuxtjs/sitemap',
'@nuxtjs/robots',
'nuxt-gtag',
// Authentication
'@sidebase/nuxt-auth',
// Content
'@nuxt/content',
],
// Konfigurasi per-module
tailwindcss: {
cssPath: '~/assets/css/tailwind.css',
configPath: 'tailwind.config.js'
},
colorMode: {
classSuffix: '',
preference: 'dark',
fallback: 'dark'
},
image: {
quality: 80,
format: ['webp', 'jpg']
},
sitemap: {
hostname: 'https://beebanelabs.pages.dev'
}
})
Modul Populer
| Modul | Fungsi | Install |
|---|---|---|
| @nuxtjs/tailwindcss | Tailwind CSS integration | npm i @nuxtjs/tailwindcss |
| @pinia/nuxt | State management Pinia | npm i @pinia/nuxt |
| @nuxt/image | Auto image optimization | npm i @nuxt/image |
| @nuxt/content | Markdown/CMS content | npm i @nuxt/content |
| @nuxtjs/sitemap | Auto sitemap generation | npm i @nuxtjs/sitemap |
| nuxt-icon | Icon library (Iconify) | npm i nuxt-icon |
| @vueuse/nuxt | VueUse composables | npm i @vueuse/nuxt |
| @nuxtjs/color-mode | Dark/light mode toggle | npm i @nuxtjs/color-mode |
Membuat Custom Module
// modules/logger.js
// Custom Nuxt module β auto-loaded dari folder modules/
import { defineNuxtModule, createResolver, addServerHandler } from '@nuxt/kit'
export default defineNuxtModule({
meta: {
name: 'logger',
configKey: 'logger'
},
defaults: {
enabled: true,
level: 'info'
},
setup(options, nuxt) {
if (!options.enabled) return
const resolver = createResolver(import.meta.url)
// Register server middleware
addServerHandler({
handler: resolver.resolve('./runtime/logger-middleware'),
middleware: true
})
console.log(`[Logger] Module aktif β level: ${options.level}`)
}
})
10. Middleware dan Layouts
Middleware di Nuxt berjalan sebelum halaman di-render, cocok untuk autentikasi, redirect, atau logging. Layouts membungkus halaman dengan struktur UI yang konsisten seperti header, sidebar, dan footer.
Route Middleware
// middleware/auth.js
// Middleware bernama β dipanggil manual di setiap halaman
export default defineNuxtRouteMiddleware((to, from) => {
const { isAuthenticated } = useAuth()
if (!isAuthenticated.value) {
// Redirect ke login jika belum autentikasi
return navigateTo('/login', {
// Simpan halaman tujuan untuk redirect setelah login
query: { redirect: to.fullPath }
})
}
})
// middleware/log.global.js
// Suffix .global = berjalan di SEMUA halaman (tidak perlu manual)
export default defineNuxtRouteMiddleware((to, from) => {
console.log(`[Route] ${from.path} β ${to.path}`)
console.log(`[Time] ${new Date().toISOString()}`)
})
Menggunakan Middleware di Halaman
<template>
<div class="dashboard">
<h1>Dashboard</h1>
<p>Selamat datang, {{ user.name }}!</p>
</div>
</template>
<script setup>
// Define middleware untuk halaman ini
definePageMeta({
middleware: ['auth'],
// Gunakan layout tertentu
layout: 'dashboard'
})
const { user } = useAuth()
</script>
Layouts
<template>
<div class="dashboard-layout">
<aside class="sidebar">
<div class="logo">
<NuxtLink to="/dashboard">Dashboard</NuxtLink>
</div>
<nav>
<NuxtLink to="/dashboard">Overview</NuxtLink>
<NuxtLink to="/dashboard/products">Produk</NuxtLink>
<NuxtLink to="/dashboard/orders">Pesanan</NuxtLink>
<NuxtLink to="/dashboard/settings">Settings</NuxtLink>
</nav>
</aside>
<main class="main-content">
<header class="top-bar">
<h2>{{ pageTitle }}</h2>
<button @click="logout">Logout</button>
</header>
<!-- Slot untuk konten halaman -->
<slot />
</main>
</div>
</template>
<script setup>
const { logout } = useAuth()
const route = useRoute()
const pageTitle = computed(() => route.meta.title || 'Dashboard')
</script>
<style scoped>
.dashboard-layout {
display: grid;
grid-template-columns: 250px 1fr;
min-height: 100vh;
}
.sidebar {
background: var(--sidebar-bg);
padding: 1.5rem;
border-right: 1px solid var(--border-color);
}
.sidebar nav {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-top: 2rem;
}
.main-content {
padding: 2rem;
}
</style>
<template>
<div class="default-layout">
<AppHeader />
<main class="main-content">
<slot />
</main>
<AppFooter />
</div>
</template>
<style scoped>
.main-content {
min-height: calc(100vh - 160px);
padding: 2rem 1rem;
max-width: 1200px;
margin: 0 auto;
}
</style>
Layout default (layouts/default.vue) digunakan secara otomatis jika halaman tidak mendefinisikan layout. Untuk halaman tanpa layout (seperti halaman login), gunakan layout: false di definePageMeta().
11. Deployment
Nuxt mendukung deployment ke berbagai platform berkat Nitro server engine. Berikut beberapa opsi populer:
Deploy ke Vercel
# Install Vercel CLI npm i -g vercel # Deploy langsung dari terminal vercel # Atau connect repository GitHub ke Vercel Dashboard # Vercel otomatis mendeteksi Nuxt dan konfigurasi build # Environment Variables # Set di Vercel Dashboard β Settings β Environment Variables # NUXT_PUBLIC_API_URL=https://api.example.com # NUXT_SECRET_KEY=your-secret-key
Deploy ke Netlify
# netlify.toml [build] command = "npm run build" publish = ".output/public" [build.environment] NODE_VERSION = "20" # Redirect SPA fallback [[redirects]] from = "/*" to = "/index.html" status = 200
Deploy dengan Node.js Server
# Build untuk production npm run build # Hasil build ada di .output/ # .output/ # βββ server/ β Node.js server # βββ public/ β Static assets # Jalankan production server node .output/server/index.m4 # Atau dengan PM2 (process manager) pm2 start .output/server/index.m4 --name nuxt-app # Environment variables NUXT_PORT=3000 NUXT_HOST=0.0.0.0 NUXT_PUBLIC_API_URL=https://api.example.com
Deploy Static (SSG)
# Generate static files npx nuxi generate # Output ada di .output/public/ # Upload folder ini ke CDN manapun: # - Cloudflare Pages # - GitHub Pages # - AWS S3 + CloudFront # - Azure Static Web Apps # Preview hasil generate secara lokal npx nuxi preview
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β NUXT DEPLOYMENT OPTIONS β β β β βββββββββββββββββ ββββββββββββββββββββββββββββ β β β SSR / Node β β Static (SSG) β β β β Server β β β β β β β β .output/public/ β β β β .output/ β β βββ index.html β β β β βββ server/ β β βββ about.html β β β β βββ indexβ β βββ blog/ β β β β β β β β β β Targets: β β Targets: β β β β β’ Vercel β β β’ Cloudflare Pages β β β β β’ Railway β β β’ GitHub Pages β β β β β’ DigitalOc. β β β’ Netlify β β β β β’ AWS EC2 β β β’ AWS S3 β β β βββββββββββββββββ ββββββββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
12. Quiz Pemahaman
Uji pemahaman Anda tentang Nuxt.js dengan quiz interaktif berikut!
1. Apa yang dimaksud dengan file-based routing di Nuxt?
2. Composable apa yang digunakan untuk data fetching SSR-safe di Nuxt?
3. Apa peran Nitro dalam Nuxt.js?
4. Bagaimana cara membuat dynamic route /blog/:slug di Nuxt?
5. Fitur Nuxt apa yang memungkinkan Anda tidak perlu menulis import statement untuk komponen?
Dalam tutorial ini, Anda telah mempelajari:
- Pengenalan Nuxt.js dan keunggulannya dibanding framework lain
- Instalasi, setup, dan struktur proyek Nuxt
- File-based routing dengan dynamic dan nested routes
- Server-Side Rendering (SSR) dan cara kerjanya
- Static Site Generation (SSG) dan hybrid rendering
- Auto-imports untuk komponen, composables, dan utils
- Membuat dan menggunakan composables
- Server API routes dengan Nitro
- Nuxt Modules dan custom modules
- Middleware dan layouts
- Deployment ke berbagai platform