Perjalanan migrasi website portfolio & blog pribadi saya ke MDX dengan Shiki dan Rehype

Selama dua tahun terakhir, saya mengandalkan Next.js, TailwindCSS, dan Markdown standar untuk menggerakkan portfolio pribadi dan blog saya. Meskipun cukup membantu, saya sering merasa frustrasi dengan keterbatasan blok kode saya. Menambahkan fitur lanjutan—seperti yang kita lihat di dokumentasi tingkat atas—terasa seperti perjuangan yang berat. Saya kehilangan:
Akar masalahnya adalah highlight.js. Meskipun populer, cukup terbatas; mengandalkan regex untuk highlighting, yang sering melewatkan nuansa sintaks modern.
Pada akhir 2025, saya akhirnya memiliki ruang untuk mengoptimalkan situs saya. Setelah menggali bagaimana situs dokumentasi modern—seperti Nextra, Code Hike, Expressive Code, dan Vite Press—menangani konten, pemenang yang jelas muncul: MDX dikombinasikan dengan Shiki dan Rehype.
Perubahan bukan hanya tentang syntax highlighting. Saya membutuhkan kemampuan untuk menyematkan komponen React interaktif langsung dalam posting saya. Markdown standar tidak bisa mengikuti.
Jalan menuju implementasi jauh dari mulus. Awalnya, saya berjuang untuk menyatukan ekosistem Shiki dan Rehype. Namun, semuanya berhasil ketika saya menemukan panduan fantastis oleh Jolly Coding.
Video tersebut memperkenalkan saya pada permata bernama Velite. Jika Anda belum mendengarnya, Velite adalah pipeline konten yang kuat yang tidak hanya "membaca" file—ia menelan, memvalidasi, mengubah, dan memperkaya konten Anda menjadi data yang ketat.
Sebelum Velite, alur saya sederhana namun rapuh:
File Markdown -> Render langsungDengan Velite, ia berubah menjadi pipeline yang kuat dan terstruktur:
Markdown/MDX
↓
Parse Frontmatter
↓
Validasi Skema (Zod)
↓
Transformasi Field
↓
Tambah Computed Fields
↓
Generate Tipe
↓
Export Konten TerstrukturBerikut adalah rincian visual tentang bagaimana Velite menangani pekerjaan berat:
Alur Kerja Velite ResmiSelain otomasi, Velite membuat konten type-safe dengan menggunakan skema Zod. Ini berarti tidak ada lagi kesalahan "undefined" atau frontmatter yang rusak. Berikut adalah gambaran konfigurasi yang saya gunakan untuk blog ini:
Setelah beberapa penyesuaian, saya berhasil membangun blog statis dengan fitur yang menyaingi dokumentasi profesional. Berikut adalah beberapa sorotan:
Isyarat visual sangat penting untuk pengalaman membaca yang baik. Saya mengimplementasikan komponen Callout—terinspirasi oleh blockquote GitHub—untuk menyoroti catatan, tips, dan peringatan.
Bagaimana tampilannya di MDX:
<Callout type="note" descriptionColor="normal">
<p>Ini adalah contoh callout catatan.</p>
</Callout>Bagaimana tampilannya saat dirender:
Note
Ini adalah contoh callout catatan.
Dengan memanfaatkan Shiki dan Rehype Pretty Code, blok kode tidak lagi teks statis—mereka adalah ruang kerja interaktif.
Tip
Dalam contoh di bawah, saya menggunakan bash sebagai bahasa untuk menampilkan kode MDX mentah tanpa dieksekusi oleh renderer. Tetapi dalam implementasi nyata menggunakan tsx sebagai bahasa.
Sintaks MDX:
```bash icon="iTsx" title="Counter.tsx" caption="Contoh counter reaktif" showLineNumbers /useState/#v /useEffect/#s /React/#i /{count}/ wrap=false wrapToggleButton
// [!code focus:1]
import React, { useState, useEffect } from 'react';
function Counter() {
// [!code focus:2]
// [!code --:1]
const [count, setCount] = useState(0);
// [!code ++:1]
const [count, setCount] = useState<number>(0);
// [!code focus:3]
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>;
}
```Hasilnya:
import React, { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [count, setCount] = useState<number>(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>;
}Salah satu penambahan paling keren adalah integrasi Twoslash. Ini membawa kekuatan compiler TypeScript langsung ke posting blog Anda.
Tip
Coba: Arahkan kursor ke atas kode yang digarisbawahi di bawah untuk melihat definisi tipe!
interface Todo {
Todo.title: stringtitle: string
}
const const todo: Readonly<Todo>todo: type Readonly<T> = { readonly [P in keyof T]: T[P]; }Make all properties in T readonlyReadonly<Todo> = {
title: stringtitle: 'Delete inactive users'.String.toUpperCase(): stringConverts all the alphabetic characters in a string to uppercase.toUpperCase(),
}
const todo: Readonly<Todo>todo.title = 'Hello'
var Number: NumberConstructorAn object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.Number.p- parseFloat
- parseInt
- prototype
NumberConstructor.parseInt(string: string, radix?: number): numberConverts A string to an integer.arseInt('123', 10)
Meskipun situs portfolio saya bersifat closed source, saya ingin memberikan kembali kepada komunitas. Saya telah membuat template open-source bernama Velora yang mencakup semua fitur ini.
Repository dilengkapi dengan dokumentasi lengkap dan pipeline CI/CD, menjadikannya titik awal yang bagus untuk proyek Anda sendiri.
Setelah menjelajahi alternatif seperti highlight.js, Prism.js, dan Code Hike, Shiki menonjol karena satu alasan utama: Akurasi.
Shiki menggunakan TextMate Grammars, mesin yang sama yang menggerakkan VS Code. Ini memastikan bahwa apa yang Anda lihat di editor Anda adalah persis apa yang pembaca Anda lihat di blog. Plus, Shiki bersifat statis. Tidak seperti highlight.js atau Prism.js, yang berjalan di browser dan dapat memperlambat, Shiki melakukan semua pekerjaan selama proses build.
Rehype Pretty Code mengambil highlighting mentah itu dan menambahkan "polish"—nomor baris, highlighting, dan atribut kustom. Dikombinasikan dengan transformers dan Twoslash, ini adalah setup kelas dunia untuk blog teknis apa pun.
Jika Anda ingin meningkatkan permainan konten Anda, migrasi ke MDX adalah investasi yang terbayar setiap kali pembaca mengklik "copy", mengarahkan kursor ke definisi tipe, dan berinteraksi dengan blok kode.