Nicholas Ly

Software Engineer

Making this website

I find myself scrolling through Twitter from time to time. Maybe more than I admit.

I'm a big fan of the "build in public" crowd on there, especially the work of designers. Probably because I primarily do backend development at my day job.

Among many designers, I've grown very fond of the work of Emil Kowalski and Jakub Krehel. Both of them have beautifully minimal personal websites that resonated with me.

I liked their websites so much I wanted to see if I could make my own. Both of their sites are closed-source, so I thought it would be a fun little challenge.

Technical decisions

Planning on starting from scratch, I had to pick my tech stack.

I chose Next.js due to my familiarity with it. I think it's a great all-in-one framework for small- to medium-sized projects.

I also chose Tailwind CSS and shadcn/ui for styling. I am not a fan of traditional CSS, and I've been a huge fan of Tailwind since I first discovered it in college. As for shadcn/ui, that was for some pre-built components and consistent theming.

Since I'm the only one writing on this site, I wanted to stick with Markdown files because it's familiar and easy to write. I didn't want to deal with setting up a , and I can take Markdown almost anywhere if I need to.

Most of the tools I've described above are pretty common. There's one new tool I recently got to experiment with, and I'll talk more about it next.

Markdown rendering

While working on The Grug Brained Chatbot, I stumbled upon the Streamdown tool by Vercel. It's a drop-in replacement for the react-markdown package, specifically designed for AI-powered streaming.

Now you're probably thinking, "This blog isn't AI-powered or streamed, what gives?"

While reading the documentation, I noticed that it has a mode for rendering static Markdown. I thought it would be great for a blog, especially since it comes with a bunch of preconfigured remark and rehype plugins out of the box.

Conclusion

In short, blog posts start as Markdown files. They're read from the filesystem and rendered with the Streamdown component. Frontmatter gets parsed using regular expressions. Everything else is just native Next.js.

I think it's pretty simple, and I'm happy with how it turned out. It's not an exact replica of Emil's website, primarily due to my laziness, but I never wanted it to be exact.