Membangun Static Site Generator dengan Astro: Kustomisasi Template dan Collection

Lhuqita Fazry
Web Development Astro Static Site Generator Content Collections Zod Schema
Membangun Static Site Generator dengan Astro: Kustomisasi Template dan Collection

Mendefinisikan Content Collection dengan Schema Validation

Salah satu fitur paling powerful di Astro adalah Content Collections — sistem resmi untuk mengelola konten terstruktur seperti blog post, dokumentasi teknis, atau halaman statis. Dengan Content Collections, kita tidak bekerja dengan file Markdown secara asal; setiap file divalidasi oleh schema di level build sehingga error frontmatter terdeteksi sebelum masuk produksi.

Setiap koleksi didefinisikan di file src/content.config.ts menggunakan fungsi defineCollection dari modul astro:content. Di sinilah kita menentukan loader — misalnya glob() untuk membaca file Markdown dari direktori tertentu — dan schema Zod yang menjamin properti seperti title, pubDate, dan description tersedia dengan tipe yang benar.

Ilustrasi konsep content collection dan validasi schema pada Astro

Gambar: Ilustrasi proses coding dan validasi data — Sumber: [Unsplash](https://unsplash.com/photos/laptop-open-on-a-wooden-desk-with-code-on-screen-bwBN3djK3GY)

typescripttypescript
// src/content.config.ts
import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";

const blog = defineCollection({
  loader: glob({ pattern: "**\/[^_]*.{md,mdx}", base: "./src/content/blog" }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.date(),
    tags: z.array(z.string()).default([]),
    heroImage: z.string().optional(),
  }),
});

export const collections = { blog };

Keuntungan utama dari pendekatan ini adalah type safety penuh. Ketika kita mengakses entry.data.title di template, editor memberikan autocomplete dan TypeScript akan menolak properti yang tidak terdefinisi di schema. Jika ada file blog yang lupa mencantumkan pubDate, proses build langsung gagal dengan pesan error yang jelas — bukan runtime crash yang sulit dilacak.

Membangun Layout Hierarchy untuk Halaman Statis

Layout di Astro adalah komponen .astro yang membungkus konten halaman melalui mekanisme <slot />. Konsep ini mirip dengan "wrapper" di framework lain, tetapi di Astro layout bisa di-nest membentuk hierarchy. Pendekatan ini menjaga konsistensi tampilan di seluruh situs dan menghindari duplikasi markup yang tidak perlu.

Kita mulai dengan BaseLayout.astro yang berisi elemen global seperti navbar dan footer. Kemudian kita buat PostLayout.astro yang membungkus BaseLayout dan menambahkan elemen spesifik seperti sidebar daftar isi atau metadata post.

Ilustrasi hirarki layout Astro dengan BaseLayout dan PostLayout

Gambar: Sketsa wireframe tata letak halaman web yang merepresentasikan konsep layout hierarchy — Sumber: [Unsplash](https://unsplash.com/photos/watercolor-wireframe-sketches-of-website-layouts-tZc3vjPCk-Q)

astroastro
---
// src/layouts/BaseLayout.astro
interface Props {
  title: string;
  description?: string;
}

const { title, description } = Astro.props;
---

<!doctype html>
<html lang="id">
  <head>
    <meta charset="UTF-8" />
    <meta name="description" content={description} />
    <title>{title}</title>
  </head>
  <body>
    <nav>{/* navigasi utama */}</nav>
    <main><slot /></main>
    <footer>{/* footer */}</footer>
  </body>
</html>
astroastro
---
// src/layouts/PostLayout.astro
import BaseLayout from "./BaseLayout.astro";

interface Props {
  title: string;
  pubDate: Date;
  tags: string[];
}

const { title, pubDate, tags } = Astro.props;
---

<BaseLayout title={title}>
  <article>
    <header>
      <h1>{title}</h1>
      <time datetime={pubDate.toISOString()}>
        {pubDate.toLocaleDateString("id-ID")}
      </time>
      <ul>{tags.map(tag => <li>{tag}</li>)}</ul>
    </header>
    <slot />
  </article>
</BaseLayout>
API Development with Golang
Web App • Beginner

API Development with Golang

A hands-on, project-based course designed to teach beginners how to build robust...

Daftar

Dengan struktur ini, setiap halaman blog post cukup menggunakan PostLayout tanpa perlu menulis ulang markup navbar, footer, atau header post. Frontmatter dari collection entry diteruskan sebagai props ke layout, memungkinkan layout menampilkan metadata secara otomatis.

Menghubungkan Collection Data ke Dynamic Routes

Setelah collection dan layout siap, langkah berikutnya adalah menghubungkan data dari collection ke halaman yang dirender secara dinamis. Astro menyediakan fungsi getCollection() untuk mengambil semua entry dari suatu koleksi, dan getEntry() untuk mengambil satu entry berdasarkan ID.

Dynamic route adalah file dengan nama [slug].astro di direktori src/pages/. File ini menggunakan getStaticPaths() untuk memberitahu Astro halaman mana saja yang harus di-generate saat build. Kita panggil getCollection('blog'), lalu mapping setiap entry ke format { params: { slug: entry.id }, props: { entry } }.

astroastro
---
// src/pages/blog/[slug].astro
import { getCollection, getEntry } from "astro:content";
import PostLayout from "../../layouts/PostLayout.astro";

export async function getStaticPaths() {
  const posts = await getCollection("blog");
  return posts.map((post) => ({
    params: { slug: post.id },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await post.render();
---

<PostLayout title={post.data.title} pubDate={post.data.pubDate} tags={post.data.tags}>
  <Content />
</PostLayout>

Yang menarik, Astro secara otomatis meng-infer tipe dari schema collection. Saat kita mengetik post.data., editor langsung menampilkan properti yang tersedia — title, description, pubDate, tags, dan heroImage. Autocomplete ini bekerja tanpa konfigurasi tambahan, berbeda dengan static site generator lain yang memerlukan definisi tipe manual.

Pada saat build, Astro memanggil getStaticPaths(), mendapatkan daftar semua blog post, dan meng-generate file HTML statis untuk masing-masing post. Hasilnya adalah halaman yang sudah siap saji tanpa perlu JavaScript di client.

Memfilter dan Mengurutkan Collection untuk Halaman Index

Halaman index blog biasanya tidak menampilkan semua post sekaligus. Kita perlu memfilter berdasarkan kategori atau tags, mengurutkan berdasarkan tanggal publikasi, dan membagi hasil ke beberapa halaman dengan pagination. Semua ini bisa dilakukan langsung di Astro tanpa bantuan library eksternal.

Fungsi getCollection() menerima callback filter sebagai argumen kedua. Kita bisa memfilter post yang memiliki tag tertentu, atau hanya menampilkan post dengan properti draft: false. Hasilnya kemudian diurutkan dengan method sort() biasa, lalu dilewatkan ke paginate().

astroastro
---
// src/pages/blog/index.astro
import { getCollection } from "astro:content";
import BlogLayout from "../../layouts/BlogLayout.astro";

export async function getStaticPaths({ paginate }) {
  const posts = await getCollection("blog", ({ data }) => {
    return data.tags.includes("tutorial") || data.tags.length > 0;
  });

  const sorted = posts.sort(
    (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
  );

  return paginate(sorted, { pageSize: 6 });
}

const { page } = Astro.props;
---

<BlogLayout>
  <ul>
    {page.data.map((post) => (
      <li>
        <a href={`/blog/${post.id}/`}>
          <h3>{post.data.title}</h3>
          <time datetime={post.data.pubDate.toISOString()}>
            {post.data.pubDate.toLocaleDateString("id-ID")}
          </time>
        </a>
      </li>
    ))}
  </ul>

  {page.url.prev ? <a href={page.url.prev}>Sebelumnya</a> : null}
  {page.url.next ? <a href={page.url.next}>Selanjutnya</a> : null}
</BlogLayout>

Kombinasi filter, sorting, dan pagination ini menghasilkan halaman index yang terorganisir tanpa hardcode. Jika suatu saat kita menambah post baru, Astro secara otomatis meng-update halaman index dan menyesuaikan pagination saat build berikutnya. Ini adalah pendekatan yang deklaratif dan mudah dipelihara.

Memperkaya Output dengan Remark dan Rehype Plugins

Astro memproses konten Markdown dan MDX melalui pipeline yang terdiri dari dua tahap: remark yang bekerja di level AST (Abstract Syntax Tree) Markdown, dan rehype yang bekerja di level HTML. Kita bisa menambahkan plugin kustom di kedua tahap ini untuk memperkaya output konten dari collection.

Ilustrasi pipeline pemrosesan Markdown melalui remark dan rehype plugins

Gambar: Alur proses transformasi konten dari Markdown ke HTML — Sumber: [Unsplash](https://unsplash.com/illustrations/two-black-arrows-in-a-circular-motion-VWdPkZ51tjY)

Misalnya, kita ingin menambahkan daftar isi otomatis pada setiap blog post dan syntax highlighting yang rapi pada code block. Dua plugin berikut — remark-toc dan rehype-pretty-code — bisa diintegrasikan langsung di astro.config.mjs.

javascriptjavascript
// astro.config.mjs
import { defineConfig } from "astro/config";
import remarkToc from "remark-toc";
import rehypePrettyCode from "rehype-pretty-code";

export default defineConfig({
  markdown: {
    remarkPlugins: [remarkToc],
    rehypePlugins: [
      [rehypePrettyCode, { theme: "github-dark-dimmed" }],
    ],
  },
});

Yang perlu diperhatikan, plugin-plugin ini hanya memengaruhi konten yang berasal dari collection — halaman non-Markdown tetap tidak tersentuh. remark-toc secara otomatis menyisipkan daftar isi berdasarkan heading di dalam Markdown, sementara rehype-pretty-code memberikan syntax highlighting dengan tema yang konsisten tanpa mengirimkan JavaScript tambahan ke browser.

Pemilihan plugin yang tepat sangat penting. Kita tidak ingin membebani bundle size dengan plugin yang tidak diperlukan. Cukup tambahkan plugin yang benar-benar meningkatkan readability konten, seperti table of contents, syntax highlighting, atau auto-link heading.

Menguasai Content Collections, layout hierarchy, dynamic routes, dan pipeline Markdown di Astro adalah fondasi untuk membangun static site generator yang profesional dan scalable. Ingin memperdalam skill Web Development modern dengan Astro dan teknologi static site lainnya? Bergabunglah dengan kursus dan bootcamp di Rumah Coding — tempat yang tepat untuk bertransformasi dari developer pemula menjadi full-stack engineer yang percaya diri.

Kursus Terkait

EduTrack: Learning Management & Assessment API
Kursus Premium Web App

API Development with Golang

A hands-on, project-based course designed to teach beginners how to build robust, high-performance RESTful APIs using Golang. Starting from core Go syntax and concurrency, students will progress through routing, database integration, security, and finally containerize their application for production deployment.

Proyek Akhir

EduTrack: Learning Management & Assessment API

  • Role-Based Access Control (RBAC)
  • Course & Module Management
  • Enrollment & Progress Tracking
7 Weeks Beginner
Lihat Detail Kursus
Asynchronous Bulk E-Certificate & Notification Engine
Kursus Premium Web App

Advanced Architecture with Laravel Containers & Queues

A problem-driven, project-based course designed to elevate intermediate developers to a senior architectural mindset. Instead of just memorizing documentation, students will tackle real-world bottleneck issues by mastering the Inversion of Control (IoC) principle, Dependency Injection, and asynchronous background processing. Learn to decouple services and orchestrate robust queues to build scalable, high-performance applications that never freeze under heavy loads.

Proyek Akhir

Asynchronous Bulk E-Certificate & Notification Engine

  • Decoupled Service Architecture: PDF generation and Email delivery are built as independent, interface-driven services injected via the Service Container.
  • Job Chaining: Ensuring processes run in strict asynchronous order (Generate PDF ➔ Upload to Storage ➔ Send Email).
  • Job Batching & Real-Time Tracking: Grouping hundreds of jobs together and displaying a live progress bar (e.g., "65% Completed") on the admin dashboard.
7 Weeks Intermediate
Lihat Detail Kursus
Mini Academic Portal (Learning Management System)
Kursus Premium Web App

Building Modern Apps with Filament

Learn how to build modern, full-stack web applications rapidly using FilamentPHP and the TALL stack. This project-based course is designed for beginners, guiding you step-by-step to create a fully functional backend administration panel. By the end of the course, you will have mastered Filament's powerful Panel Builder, robust CRUD operations, complex database relationships, and interactive dashboards.

Proyek Akhir

Mini Academic Portal (Learning Management System)

  • Master Data Management: Comprehensive CRUD functionality to manage Instructors and Course catalogs.
  • Student Registry: A dedicated module to manage student profiles and personal information.
  • Dynamic Enrollment System: Handling Many-to-Many relationships to enroll students into specific courses using Filament's pivot features.
7 Weeks Intermediate
Lihat Detail Kursus

Artikel Terkait