Agus Wira.
← Kembali ke proyek
PortfolioAstroMDXFrontend

Portfolio Website

Website pribadi untuk proyek, tulisan, dan perjalanan belajar Informatika. Arsitekturnya dimigrasikan dari Next.js ke Astro agar halaman konten tetap statis, ringan, dan tidak membawa runtime React.

Role
Solo designer dan developer — menyusun konten, design system, frontend, motion, endpoint server, serta deployment.
Timeline
22 Juni 2026–sekarang — versi awal, audit menyeluruh, case study MDX, lalu migrasi bertahap dari Next.js ke Astro.

Stack

  • Astro 7
  • TypeScript
  • Tailwind CSS
  • GSAP ScrollTrigger
  • Astro Content Collections
  • MDX + Zod
  • Upstash Redis
  • Resend
  • OpenAI-compatible API

Problem

Versi pertama website ini sebenarnya sudah punya banyak bagian: hero, profil, daftar skill, proyek, blog, form kontak, sampai AI chat. Dari luar kelihatan lengkap, tetapi bagian proyeknya belum banyak membantu.

Setiap proyek hanya mendapat satu kartu dengan judul, deskripsi singkat, stack, dan link. Kalau seorang dosen, recruiter magang, atau pembaca teknis ingin tahu apa yang sebenarnya kukerjakan, mereka harus keluar dari website dan membongkar repository sendiri.

Aku ingin portfolio ini lebih dari halaman perkenalan. Setiap proyek seharusnya bisa menjawab beberapa pertanyaan dasar: masalah apa yang dikerjakan, apa peranku, bagaimana arsitekturnya, hasil apa yang benar-benar ada, dan bagian mana yang masih kurang.

Approach

Aku memulai perombakan dari konten, bukan tampilan. Data proyek yang sebelumnya berada di array TypeScript dipindahkan ke MDX. Frontmatter menyimpan informasi yang perlu tampil konsisten pada kartu dan header, sedangkan body MDX dipakai untuk cerita yang lebih panjang.

Semua case study memakai urutan yang sama: Problem, Approach, Architecture, Outcome, lalu Lessons. Urutan ini bukan supaya setiap tulisan terdengar seragam, tetapi supaya pembaca tahu di mana harus mencari informasi.

Homepage menampilkan cover proyek dan ringkasan singkat pada setiap kartu agar pengunjung bisa mengenali konteksnya dengan cepat. Outcome yang lebih rinci tetap berada di case study supaya kartu tidak berubah menjadi dinding teks.

Astro Content Collections memeriksa frontmatter sebelum build. Slug harus valid, field wajib tidak boleh kosong, dan body MDX tidak boleh menambahkan H1 sendiri. H1 dibuat oleh route detail. Guard terakhir ditambahkan karena halaman blog pernah menghasilkan dua H1 pada halaman yang sama.

Saat menulis isi proyek, aku memakai source code, riwayat Git, hasil test, deployment record, dan URL publik sebagai rujukan. Informasi yang hanya bisa datang dari pengalaman pribadi tetap dibiarkan kosong sampai aku bisa menuliskannya sendiri.

Architecture

src/content/
  +-- blog/*.mdx ----------> Content Collection --> /blog/[slug]
  |
  +-- projects/*.mdx ------> Content Collection --> /projects/[slug]
                                     |
                                     +------------> homepage project cards

Astro
  +-- Static pages: layouts, sections, metadata, MDX rendering
  +-- Vanilla TS: navigation, filters, contact, motion, chat shell
  +-- Server endpoints:
        +-- /api/chat -----> Upstash --> OpenAI-compatible SSE
        +-- /api/contact --> Upstash --> validation --> Resend

Astro menangani route, layout, dan prerendering. Content Collections membaca blog serta proyek dari filesystem, memvalidasi frontmatter dengan Zod, lalu menghasilkan static path. MDX dirender saat build dengan GitHub-flavored Markdown dan syntax highlighting. Homepage hanya memakai metadata kartu; body panjang tidak masuk ke JavaScript filter.

Bagian visual tetap memakai Tailwind dengan warna Nord. GSAP ScrollTrigger hanya dimuat di homepage untuk reveal dan parallax, sedangkan marquee memakai CSS. Navigasi, filter, spotlight, contact, dan shell chat memakai vanilla TypeScript. Reduced motion menghentikan animasi berulang dan menonaktifkan transisi yang tidak diperlukan.

Chat dan form kontak menjadi endpoint Astro on-demand karena membutuhkan secret. Keduanya dibatasi dengan sliding window Upstash yang memakai identitas IP ter-hash dan namespace environment. Chat meneruskan SSE dari provider OpenAI-compatible; contact memvalidasi input dan mengirim email lewat Resend. Request memiliki batas ukuran, timeout upstream, serta status error yang tidak membocorkan detail internal.

Outcome

Website publik dapat dibuka di aguswira.dev, sedangkan source-nya tersedia di GitHub.

Pada build Astro, seluruh slug blog dan proyek dibuat sebagai static page. Setiap halaman memiliki satu H1, canonical URL, social metadata, serta structured data sesuai jenis kontennya.

RouteRenderingJavaScript eager (gzip)
/Staticsekitar 47 kB
/blog/[slug]Staticsekitar 0,9 kB
/projects/[slug]Staticsekitar 0,9 kB
/api/*Vercel serverless

Vitest mencakup schema, validasi API, rate-limit key, timeout, abort, provider, dan parser SSE. Playwright memeriksa keyboard flow, reduced motion, axe, contact, dan streaming chat di viewport desktop serta mobile tanpa memanggil provider live.

Implementasi Astro kini menjadi versi utama. Perubahan berikutnya tetap melewati build, audit performa, Preview Vercel, dan smoke test sebelum dirilis.

Lessons

Audit ini mengubah cara aku menilai arsitektur portfolio. Framework dan animasi bukan tujuan; keduanya berguna hanya jika halaman tetap cepat, mudah dinavigasi, dan membantu pembaca menemukan bukti proyek.

Migrasi bertahap juga terasa lebih aman daripada rewrite sekaligus. Setiap fase punya parity check, test, build, dan commit sendiri. Sumber Next dipertahankan sebagai referensi sampai halaman, interaksi, dan API Astro terbukti setara, baru kemudian dihapus.

Pelajaran teknis terbesarnya adalah menjaga JavaScript sebagai pilihan per route. Halaman konten hanya memuat script kecil untuk navigasi global, homepage memuat interaksi yang terlihat, dan parser streaming chat masuk ke lazy chunk setelah pengguna benar-benar mengirim pesan. Hasil performa akhir tetap harus dibuktikan lewat Lighthouse dan Preview production-like, bukan hanya ukuran bundle lokal.