18 July, 2024

Remake my blog - No more WordPress

I decided to re-code this blog. No more WordPress, so what am I using now?

Remake my blog - No more WordPress
Available in:
 English
 Vietnamese
Reading time: 5 min.
Table of content

    I've been writing this blog since around 2016, and now I have about 60 articles. As I'm about to join a new company, I want to focus more time on sharing experiences and personal perspectives through writing.

    To optimize this work, I need more special features on my blog. However, I realized that the current version, running on WordPress, has quite a few limitations.

    So if I'm not using WordPress anymore, what am I going to use?

    (Image: interface when using WordPress)

    Brainstorming

    Why am I leaving WordPress?

    To be honest, WordPress isn't bad. But the truth is that every technology has its specific limitations. You'll see that most of the reasons I give here are because I require somewhat "advanced" features that WordPress can do, but just not as well as coding it myself:

    Multilingual: Although plugins like Polylang can be passable (I've used this plugin on 2 other websites), working with Polylang is often quite time-consuming to set up. On the homepage ngxson.com, I had to code the entire multilingual part myself.

    Markdown support: This reason is simpler, I'm using markdown more and more, so switching to a platform like Jekyll makes sense.

    Extended interaction: Taking inspiration from science blogs that allow users to interact directly with the website, I want to be able to do some cool things like 3D visualization in the future. An example of an interactive article: https://distill.pub/2017/feature-visualization/

    Speed: Although you can install a bunch of plugins and caches to make WordPress faster, I think it's better to use something lighter and more efficient in the long run.

    The Plan

    Some must-have requirements for this new blog:

    • Multilingual (as efficiently as possible)
    • Based on markdown
    • Modern, simple, and intuitive interface
    • Light in size, fast in speed
    • Long-term extensibility

    Based on this list, I've outlined the direction I could take:

    • Program based on NextJS and build into static files (hosted on CDN)
    • Use MDX, an extension of markdown, allowing writing some ReactJS code right on markdown
    • One file per article, one file per language
    • Use daisyUI and tailwindcss to maintain stable interface design in the long run
    • Possibly use AI to translate articles into other languages

    Let's get to work!

    Design on Figma

    The first step I took wasn't to dive into coding, but to open Figma to create a Moodboard (explanation: it's simply as a page to copy-paste ideas for reference)

    After some research, I was quite impressed with the designs of Medium, OpenAI blog, and Apple Newsroom. These pages are both simple and convenient.

    For the font, I chose a combination of serif and sans-serif fonts to evoke a bit of nostalgia:

    Next, I designed what my blog should look like:

    Pretty good, huh? Now it's time to code.

    Setup NextJS

    The first thing to do is to set up a framework to manage and render markdown files into HTML. I'll need to use NextJS's generateStaticParams to do this.

    The code looks roughly like this, with DataSource being the class I use to list all files:

    export async function generateStaticParams() {
      return await ParamsGenerator.article();
    }
    
    // ParamsGenerator
    export const ParamsGenerator = {
      async article(): Promise<ParamsArticle[]> {
        const articles = await DataSource.getAll();
        return articles.map((p) => ({ slug: p.slug }));
      },
      // ...
    }
    

    There's also code to manage multilingual content, categories, pagination... The logic isn't too complex to explain, but when you start coding, you'll find many interesting things in the implementation.

    UI coding

    I used daisyUI and tailwindcss:

    Fortunately, the available components are quite close to what I need, so I didn't have to modify too much. For example, with the card to contain the article, the code looks like this:

    <Link
      href={getArticleURL(article)}
      className={`group card bg-base-200 w-full shadow-xl mt-4 hover:cursor-pointer`}
    >
      <figure>
        <img
          className={`group-hover:scale-105 duration-100 aspect-social object-cover`}
          src={article.coverImg}
          alt={article.title}
        />
      </figure>
      <div className="card-body">
        <p className="flex-grow-0">{CATEGORY[article.category[0]].name}</p>
        <h2 className={`flex-grow font-serif font-bold text-2xl`}>
          {article.title}
        </h2>
        <div className="card-actions justify-end flex-col">
          <p>{formatDateToStr(article.datePublish)}</p>
        </div>
      </div>
    </Link>
    

    Converting all old posts to markdown

    One of the biggest problems I faced was that the old posts were completely in pure WordPress. Converting them manually to markdown was quite laborious, so I wrote a script to automate it, then used node-html-markdown to convert HTML to markdown.

    Because the image sizes in the old blog and the new blog are different, I also asked Claude AI to write a small script to enlarge the cover image, with the background using a blurred image, like this:

    Translating articles with Claude AI

    I didn't use ChatGPT because I felt the translation quality wasn't as "smooth". Currently, about 8 articles have English versions.

    Bonus: article suggestions using vectors

    Simple requirement: at the end of each article, I want to suggest other articles with similar topics. Of course, I could do this by randomly selecting by topic, but that's not interesting.

    The way I do it here is to calculate the embedding vector (understood simply as a vector containing the meaning) of the article, then calculate the distance to see which vector is closest. If the vectors are close, it means the two articles have similar meanings, and therefore can be used as suggestions.

    I use OpenAI Embeddings API to do this:

    Deploying to server

    The final step, I deployed to Netlify and linked it to my domain. The build process takes place directly on the cloud, and it's quite fast (usually about 1 minute):

    The Result

    So after 5 evenings of work, I finally completed it. The work wasn't too technically difficult, it just had many stages, and there were steps I had to do manually.

    But thanks to that, my blog is now ready for higher quality articles. Stay tuned!

    Want to receive latest articles from my blog?