Peralatan yang digunakan:
- Nuxt 3.17
- Nuxt Content v3.4.0
- NuxtUI v3.1.0 (Tailwind CSS termasuk dalam paket ini)
- GitLab Pages
Memulai proyek
Mari buat proyek Nuxt baru menggunakan baris perintah berikut.
npm create nuxt nuxt-blog
Pilih jawaban sesuai dengan panduan berikut. Gunakan tombol panah untuk memilih dan tekan enter
untuk menetapkan jawaban dan melanjutkan proses.
❯ Which package manager would you like to use? ● npm ○ pnpm ○ yarn ○ bun ○ deno
❯ Initialize git repository? ● Yes / ○ No
Tekan spasi
untuk memilih dan tekan enter
untuk menetapkan jawaban dan melanjutkan proses.
❯ Would you like to install any of the official modules? ◼ @nuxt/content – The file-based CMS with support for Markdown, YAML, JSON ◼ @nuxt/eslint – Project-aware, easy-to-use, extensible and future-proof ESLint integration ◼ @nuxt/fonts – Add custom web fonts with performance in mind ◼ @nuxt/icon – Icon module for Nuxt with 200,000+ ready to use icons from Iconify ◻ @nuxt/image – Add images with progressive processing, lazy-loading, resizing and providers support ◻ @nuxt/scripts – Add 3rd-party scripts without sacrificing performance ◻ @nuxt/test-utils – Test utilities for Nuxt ◼ @nuxt/ui – The Intuitive UI Library powered by Reka UI and Tailwind CSS
Setelah pemasangan selesai, buka proyek dengan penyunting teks atau IDE. Saya menggunakan Zed sebagai contoh.
# masuk ke direktori proyek
cd nuxtjs-blog
# buka dengan Zed
zed .
Mulai aplikasi menggunakan terminal bawaan dari Zed.
npm run dev
Buka peramban web dan pergi ke http://localhost:3000 untuk melihat aplikasi.
Mengatur Tailwind CSS
Selanjutnya adalah mengatur NuxtUI dan Tailwind CSS pada proyek kita. Kita akan menggunakan komponen Switch untuk membuat tombol saklar tema terang dan gelap agar pembaca bisa menentukan sendiri mana yang lebih nyaman baginya.
Pertama, mari kita buat berkas assets/css/main.css
.
@import "tailwindcss";
@import "@nuxt/ui";
Kemudian tambahkan pengaturan css
pada berkas nuxt.config.ts
seperti berikut.
import tailwindcss from "@tailwindcss/vite";
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: "2024-11-01",
devtools: { enabled: true },
modules: ["@nuxt/content", "@nuxt/eslint", "@nuxt/fonts"],
css: ["~/assets/css/main.css"],
});
Pasang ikon
Tombol saklar untuk tema memerlukan ikon untuk memberitahu kepada pembaca tema mana yang sedang aktif. NuxtUI menyarankan untuk memasang kumpulan ikon secara lokal agar dapat disajikan langsung dari komputer atau fungsi dari serverless. Kali ini kita akan menggunakan ikon dari lucide
. Silakan baca dokumentasi ikon NuxtUI untuk informasi lebih lanjut jika Anda ingin menggunakan ikon yang lain.
npm install @iconify-json/lucide
Mengatur tata letak
Tombol saklar tema
Buat berkas components/ColorModeToggle.vue
.
<script setup lang="ts">
const colorMode = useColorMode();
const isDarkMode = computed({
get: () => colorMode.value === "dark",
set: (_isDark) => (colorMode.preference = _isDark ? "dark" : "light"),
});
</script>
<template>
<ClientOnly v-if="!colorMode?.forced">
<div class="flex items-center">
<UIcon name="i-lucide-sun" />
<USwitch
v-model="isDarkMode"
class="mx-2"
aria-label="color mode switch"
/>
<UIcon name="i-lucide-moon" />
</div>
</ClientOnly>
</template>
Tata letak halaman
Karena kita hanya menggunakan tata letak tunggal untuk setiap halaman, kita akan menggunakan berkas app.vue
sebagai berkas untuk tata letak. Lapisi komponen NuxtPage
dengan komponen UApp
untuk menerapkan gaya dari NuxtUI dan juga komponen UContainer
untuk menetapkan lebar isi halaman. Lebar bawaan dari UContainer
adalah sekitar 1200px dan mungkin Anda ingin mengubahnya agar sesuai dengan gaya yang Anda inginkan. Silakan baca dokumentasi komponen Container NuxtUI untuk keterangan lebih lanjut.
<template>
<div>
<NuxtRouteAnnouncer />
<NuxtLoadingIndicator />
<UApp>
<UContainer>
<nav class="flex justify-between py-4">
<NuxtLink to="/" class="decoration-dotted underline-offset-8 hover:underline hover:text-primary">
Blog
</NuxtLink>
<ColorModeToggle />
</nav>
<NuxtPage />
</UContainer>
</UApp>
</div>
</template>
Membuat halaman beranda
Tata letak sudah siap dan sekarang kita akan mengatur pengaturan isi sebelum menampilkan data di halaman kita. Nuxt akan mengolah semua jenis berkas yang berada di dalam direktori content
dan kita menginginkan hanya berkas Markdown yang akan diolah. Buat berkas content.config.ts
pada direktori utama proyek dengan kode sebagai berikut.
import { defineCollection, defineContentConfig, z } from "@nuxt/content";
export default defineContentConfig({
collections: {
articles: defineCollection({
type: "page",
source: "**/*.md",
schema: z.object({
title: z.string(),
date: z.string(),
}),
}),
},
});
Di sini kita menggunakan articles
sebagai nama kumpulan. Tentu saja kita dapat menggunakan kata lain sebagai nama kumpulan seperti posts
atau notes
. Karena kita akan menerbitkan artikel untuk blog, kita tetap menggunakan articles
sebagai nama kumpulan.
Properti obyek schema
harus hadir pada bagian front matter di dalam berkas Markdown sehingga kita dapat menggunakannya pada berkas Vue.
Baik, mari kita buat artikel pertama kita dengan berkas content/artikel-pertama.md
.
---
title: Artikel pertama
date: 2025-05-06
---
# Pendahuluan
Blog ini dibangun menggunakan:
- [Nuxt](https://nuxt.com) 3
- [Nuxt Content](https://content.nuxt.com) v3.4.0
- [NuxtUI](https://ui.nuxt.com) v3.1.0
Anda dapat membuat beberapa berkas lainnya jika Anda menginginkannya.
Data kita sudah siap dan mari kita tampilkan di halaman beranda. Buat berkas pages/index.vue
.
<script setup lang="ts">
const { data: posts } = await useAsyncData("posts", () =>
queryCollection("articles").order("date", "DESC").all(),
);
</script>
<template>
<main>
<h1 class="font-bold mb-8 text-3xl">Blog</h1>
<article
v-for="post in posts"
:key="post.id"
class="flex flex-col gap-2 mb-4"
>
<NuxtLink
:to="{
name: 'blog-slug',
params: { slug: post.path.replace('/', '') },
}"
class="decoration-dotted text-2xl text-primary underline-offset-4 hover:underline"
>
{{ post.title }}
</NuxtLink>
<NuxtTime
:datetime="post.date"
year="numeric"
month="long"
day="numeric"
locale="id"
class="text-sm"
/>
</article>
</main>
</template>
Anda akan melihat daftar artikel yang berada di halaman beranda. Mungkin Anda perlu menyalakan ulang server untuk melihat perubahan.
Menampilkan halaman artikel
Ketika membuat halaman beranda, Anda mungkin akan melihat pesan galat bahwa halaman blog tidak tersedia atau semacamnya. Ini terjadi karena kita belum membuat halaman untuk menampilkan artikel. Pada proyek ini kita ingin URL ke artikel terlihat seperti /blog/artikel-pertama
. Itulah mengapa properti name
pada NuxtLink
adalah blog-slug
. Sedangkan slug
adalah nama pola URL untuk artikel sehingga nanti kita akan membuat berkas bernama [slug].vue
.
Mari kita buat berkas pages/blog/[slug].vue
.
<script setup lang="ts">
const slug = useRoute().params.slug;
const { data: article } = await useAsyncData(`article-${slug}`, () =>
queryCollection("articles").path(`/${slug}`).first(),
);
useSeoMeta({
title: article.value?.title + " | Blog",
});
</script>
<template>
<main>
<header v-if="article" class="mt-24 mb-12">
<h1 class="font-bold mb-4 text-4xl">{{ article.title }}</h1>
<NuxtTime
:datetime="article.date"
year="numeric"
month="long"
day="numeric"
locale="id"
/>
</header>
<ContentRenderer v-if="article" :value="article" tag="article" />
<div v-else>
<h1 class="font-bold mb-8 text-4xl">Halaman tidak ditemukan</h1>
<NuxtLink to="/">Kembali ke blog</NuxtLink>
</div>
</main>
</template>
Sekarang cobalah untuk menekan judul dari artikel. Seharusnya Anda akan diarahkan ke halaman artikel. Anda mungkin perlu menyalakan ulang server untuk melihat perubahan atau jika terjadi galat.
Mengatur gaya komponen Prose
Isi dari Markdown ditampilkan menggunakan komponen Prose dan bawaannya tidak memiliki gaya apapun. Awalnya terasa merepotkan dan seingat saya dulu Tailwind memiliki kumpulan gaya prose
yang memudahkan. Dugaan saya mengapa Tailwind melakukan ini adalah untuk melepas ketergantungan dengan pihak, vendor, atau tata cara tertentu sehingga pengguna lebih leluasa dalam mengatur gaya.
Kita dapat mengubahsuai komponen-komponen Prose dengan membuat komponen dengan nama yang sama di dalam direktori /components/content
. Salin sebarang komponen yang akan Anda gunakan dari @nuxtjs/mdc
kemudian atur gaya sesuai dengan keinginan Anda.
Menambahkan favicon
Sebelum kita memasang blog kita, mari kita buat favicon untuk blog kita. Silakan buat favicon dari favicon.io dan salin isinya ke direktori public
.
Tambahkan pengaturan app
pada berkas nuxt.config.ts
.
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
app: {
head: {
title: "Blog",
htmlAttrs: {
lang: "en",
},
meta: [
{
name: "description",
content: "Selamat datang di blog saya",
},
],
link: [
{
rel: "apple-touch-icon",
sizes: "180x180",
href: "/apple-touch-icon.png",
},
{
rel: "icon",
type: "image/png",
sizes: "32x32",
href: "/favicon-32x32.png",
},
{
rel: "icon",
type: "image/png",
sizes: "16x16",
href: "/favicon-16x16.png",
},
{ rel: "manifest", href: "/site.webmanifest" },
],
},
},
});
Pemasangan
Sekarang blog kita siap untuk dipasang. Sebelumnya saya telah mencoba memasang blog ini ke beberapa penyedia layanan. Berikut adalah pengalaman saya memasang blog di Cloudflare dan GitLab Pages.
Cloudflare
Untuk pemasangan yang mudah dan tidak bertele-tele, Cloudflare Pages merupakan pilihan yang bagus. Memasang halaman statis di Clouflare Pages membuat situs web Anda dimuat lebih cepat karena CDN atau jaringan edge milik Cloudflare. Salah satu kekurangan dari pemasangan di Clouflare adalah Anda akan mendapatkan nama yang agak acak jika nama proyek Anda menggunakan nama umum, misalnya blog-s92jws.pages.dev
. Anda perlu berlangganan paket Business untuk menggunakan domain ubahsuai jika tidak menggunakan pengelolaan domain milik Clouflare.
Sebelum melanjutkan pemasangan ke Clouflare, tambahkan pengaturan nitro
pada berkas nuxt.config.ts
untuk mencocokkan perutean milik Clouflare.
export default defineNuxtConfig({
nitro: {
prerender: {
autoSubfolderIndex: false
}
}
})
Ketika memasang ke Clouflare, versi Node.js bawaannya masih menggunakan versi 18 LTS ketika tulisan ini dibuat (Mei 2025) dan proyek ini menggunakan Node.js versi 22 LTS. Atur versi Node.js yang akan digunakan dengan menetapkan variabel lingkungan.
NODE_VERSION=22.15.0
Karena situs web yang akan kita bangun bersifat statis, maka kita perlu mengubah perintah untuk build.
Field | Value |
---|---|
Build command | npm run generate |
Publish directory | dist |
Setelah pemasangan selesai, lihat situs web Anda pada URL yang disediakan oleh Clouflare.
GitLab Pages
Alternatif dari Clouflare Pages adalah GitLab Pages. Situs web Anda mungkin tidak akan dimuat secepat ketika dipasang di Clouflare (masih bisa diterima, tidak terlalu cepat atau terlalu lamban) dan Anda bisa menggunakan domain ubahsuai secara gratis sekaligus sertifikat SSLnya!
Proses pemasangannya cukup sederhana, petunjuk yang disampaikan di dokumentasi GitLab Pages dan Nuxt juga jelas. Pastikan Anda sudah mencocokkan pengaturan dari kedua sumber tersebut.
Setelah selesai pemasangan, GitLab akan menambahkan sebuah berkas gitlab-ci.yml
di repositori Anda. Tarik perubahan dari repositori ke komputer Anda untuk menghindari konflik pada kegiatan Git selanjutnya.
Untuk meningkatkan performa situs web dan proses build, perbarui berkas gitlab-ci.yml
hingga seperti kode berikut.
# The Docker image that will be used to build your app
image: node:lts
create-pages:
pages:
# The folder that contains the files to be exposed at the Page URL
publish: .output/public
rules:
# This ensures that only pushes to the default branch will trigger
# a pages deploy
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
cache: # Cache modules using lock file
key:
files:
- package-lock.json
paths:
- .npm/
# Functions that should be executed before the build script is run
before_script:
- npm ci --cache .npm --prefer-offline
script:
- npm run generate
- find .output/public -type f -regex '.*\.\(htm\|html\|xml\|txt\|text\|js\|css\|svg\)$' -exec gzip -f -k {} \;
- find .output/public -type f -regex '.*\.\(htm\|html\|xml\|txt\|text\|js\|css\|svg\)$' -exec brotli -f -k {} \;
Kita menambahkan pengaturan cache
untuk mempercepat proses build dan mengompres aset menggunakan gzip
dan brotli
untuk mempercepat pengunduhan sehingga situs Anda dimuat lebih cepat.
Kesimpulan
Jika semuanya berjalan dengan lancar, alhamdulillah. Kita sudah berhasil membuat blog menggunakan Nuxt. Kita sudah membangun kemampuan dasar seperti pengelolaan isi dan selebihnya gunakan kreatifitas Anda untuk membuat tampilan web yang menarik dan terasa personal!
Setelah situs web Anda jadi, coba juga ukur kinerjanya menggunakan PageSpeed Insights. Dapatkah Anda mendapat nilai yang sempurna?
Jika Anda memerlukan inspirasi, silakan intip repositori situs web ini.
Daftar pustaka
- Building a Blog with Nuxt Content - Part 1 | Mastering Nuxt
- Install Tailwind CSS with Nuxt | Tailwind CSS
- NuxtLayout - Nuxt Directory Structure | Nuxt
- Building a modern blog | dev.to
- Build a blog using nuxt and sanity | Cloudflare
- Nuxt Todo App with Auth | GitHub
- Deploy Nuxt to Cloudflare | Nuxt
- Build configuration - Clouflare Pages docs | Cloudflare
- Mastering Prose Components on Nuxt | Mastering Nuxt
- Remove leading white space on pre code | Brave Answers
- Set up a partial CNAME zone | Cloudflare DNS Docs
- Deploy Nuxt to GitLab Pages | Nuxt
- GitLab Pages Compressed Assets | Daniel Pietzsch
- GitLab Pages settings | GitLab Docs
- PageSpeed Insights