Back

First Time Configuring MDX

Completing my personal portfolio website by creating a personal blog site using MDX

6 minute
June 22, 2026

What is MDX? 🤔

Imagine if you want a write something and you have a pen and paper ready. And you have an intention to write your things and share it with others. But the problem is your paper is not just an ordinary paper, it is a glossy paper that a pen shouldn't be the best choice for the equipment you want write it on. A pen tint on a glossy paper will make your writings invisible and not readable to others. So the solution is you need the suitable equipment for your writings so it will not collapse and great for others to see.


The same thing as MDX, it is a solution for anyone who wants to convert their own writings into a JavaScript element. MDX use markdown typed approached to render an JavaScript element and React friendly environments, so it is widely popular for someone who wants to build their own personal blogs or document their writings as in their personal website which is an effective method to use.


What is so special about MDX? ✨

MDX (Markdown eXtension) has widely popular for technical writings such as programmers who want to make their own documentations while keeping an interactive experience for users. The speciality of MDX is that you treat your own writings like an HTML tag with purely flexible interactive layouts and components. Although purely it's obvious MDX use a markdown typed formatting, React has made MDX much easier to develop and plugged within your website.


Besides the robustness, MDX had also a turning trade off's. Because MDX compiles markdown into a React component, which adds to your JavaScript bundle resulting overhead risks and regressed performance. There are also security concerns such as MDX allows executable code inside, which can lead to script injections by user-submitted MDX.


How i build the MDX system on this website?

I spent almost a week building the MDX systems without touching as called "vibe coding" approach. To understand what MDX are, I stepped into a grey area and where I learned that MDX are running on JavaScript bundle. MDX is super easy to develop if you are using a React Framework such as Next.js (which I currently use on this website). Next.js itself has a built-in library for MDX that allows you to convert MDX directly into a JavaScript element. The idea was simple:

  1. Choose a path to store MDX content as a ".mdx" file
  2. Listen to that relative path, do asynchronous feedback for updates and react builds
  3. Create a MDX components to restyle MDX tags

After choosing the relative path for storing each content, I created an utility function for retrieving all content to be used on displaying all the blogs I created. Simply search the exact relative path i stored which is src/content while mapping each file on the path and separates the metadata in the .mdx files using matter from gray-matter. In this function I did also add some simple function to calculate automatically how much minutes it took for the content to be readed and also return the primary metadata of each files.

TS
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import useMDXComponents from '@/app/mdx-components';
 
const BLOG_PATH = path.join(process.cwd(), "src/content");
 
export function getAllPosts() {
    return fs.readdirSync(BLOG_PATH).map((file) => {
        const source = fs.readFileSync(path.join(BLOG_PATH, file), 'utf-8');
 
        const { content, data } = matter(source);
 
        const readingTime = calcReadingTime(content);
 
        const formattedDate = data.date?.toLocaleDateString("en-US", {
            month: 'long',
            day: 'numeric',
            year: 'numeric',
        });
 
        return {
            ...data,
            slug: file.replace(".mdx", ""),
            title: data.title,
            description: data.description,
            date: formattedDate,
            readingTime
        };
    });
}

But this is not enough, I needed to render the specific content as the MDX itself and return it so I can display it on the webview of this page. So I built another utility function to retrieve an exact post by using a namefile as it slug.

TS
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import useMDXComponents from '@/app/mdx-components';
import { compileMDX } from 'next-mdx-remote/rsc';
import rehypePrettyCode from 'rehype-pretty-code';
 
type Props = {
    params: Promise<{
        slug: string
    }>
};
export async function getPost({params}: Props) {
    const { slug } = await params;
    
    const filePath = path.join(process.cwd(), "src/content", `${slug}.mdx`);
    
    const { content, data } = matter(fs.readFileSync(filePath, "utf-8"));
 
    const { content: mdx } = await compileMDX({
        source: content,
        options: {
            mdxOptions: {
                rehypePlugins: [
                    [
                        rehypePrettyCode,
                        {
                            theme: "github-dark",
                        }
                    ]
                ]
            }
        },
        components: useMDXComponents({})
    });
 
    const readingTime = calcReadingTime(content);
 
    const formattedDate = data.date?.toLocaleDateString("en-US", {
         month: 'long',
        day: 'numeric',
        year: 'numeric',
    });
 
    return {
        ...data,
        mdx,
        title: data.title,
        description: data.description,
        date: formattedDate,
        readingTime,
    }
 
}

Noticed that I used rehypePrettyCode library in the function, which that library converts a code block with syntax highlighting (just that LoL). While this function almost returning and doing the same thing as the last function before, this function will do asynchronous work while retrieve the slug params so it can be used as in the dynamic routes. And notice a difference with this function? yes, it compiles the MDX files and return the source of content so it can be displayed.


I also add a separate function to use as MDX components to restyle markdown tags by defining each tag or trigger so it looks cool and align with the website. It is super simple on what I experience with Next.js, I just need to define components as MDXComponents within the mdx/types library.

TS
import type { MDXComponents } from "mdx/types";
import Callout from "./blog/[slug]/components/callout";
import CodeBlock from "./blog/[slug]/components/code-block";
 
export default function useMDXComponents(components: MDXComponents): MDXComponents {
    return {
        Callout,
        figure: CodeBlock,
        h1: ({children}) => (
            <h1 className="font-bold text-2xl">
                {children}
            </h1>
        ),
        ...components
    };
}