DEV Community

Cover image for I completely rewrote my personal website using as a CMS
James Wallis
James Wallis

Posted on • Updated on • Originally published at

I completely rewrote my personal website using as a CMS

The final weekend of January 2021 was uneventful in comparison with other years - in the UK we were in full lockdown due to the Coronavirus. It was, however, the perfect opportunity to completely rewrite my personal website.


I decided to redesign and rewrite my website for several reasons:

  • I wanted to move from JavaScript to TypeScript.
  • The website was styled using styled-jsx, which can be a pain to maintain and IMO is a bit messy. At the moment I'm using Tailwind CSS and so far loving it and its utility-style nature; I wanted my personal website to reflect this.
  • I no longer liked the design and wanted it to be cleaner and simpler.
  • I wanted my blog and portfolio to be loaded dynamically from a CMS rather than having to copy+paste a new page for each entry - See the Originally published at at the top of this article.

The old home page

My old home page

Technologies used

  • TypeScript - Since being introduced to TypeScript at work, I've started to understand the benefits over plain JavaScript.
  • Next.js - I don't try to hide the fact that I love Next.js, it's so simple to use and to date most of my articles contain Next.js in some way.
  • Tailwind CSS - Lately I've been using Tailwind CSS heavily. To quote their homepage, it enables me to "rapidly build modern websites without ever leaving [my React component]". Tailwind CSS also made it incredibly easy to add a dark mode. Also Tailwind Typography.
  • API to dynamically build the blog and portfolio pages ⬅️ My favourite feature.

Using as a CMS

My favourite part of my website is the use of as a Content Management System for the blog and portfolio pages. I've seen the API utilised before to display a user's articles on their website but, AFAIK, not quite in the same way as I've applied it.

The benefits of using as a CMS are:

  1. stores the articles and any images that are uploaded and used.
  2. I can use's editor and the ability to draft an article and publish it later.
  3. Has a built-in RSS feed that I can use to share my articles to other sites such as CodeNewbie and Medium.
  4. Although has the article first, the use of a canonical URL ensures that Google and other sites see my personal website as the source site for the articles.
  5. Converts the article into HTML for me. I ended up rendering the HTML from the article markdown myself, as it required fewer requests to the API.


Before I continue I want to stress that I intend to use purely for my blog and portfolio (past projects / showdev). I won't be using to create pages which are not articles and would cause to become cluttered with spam if others follow suit. For example, the about section on the home page is hardcoded into the website and if I created a page for my education history, I'd keep that purely for the website and wouldn't post it to - I'd probably use Markdown for these.

How it works

View the code on GitHub

Built using Next.js, the website uses two dynamic routing functions (getStaticPaths and getStaticProps) to generate the blog and portfolio pages.

Before an article is displayed on my website, it must meet the two following requirements:

  1. Must be published (obviously)
  2. Must have a canonical URL directing to my website. This enables me to pick which articles are displayed, what the article's path will be on my website (not the post ID). Moreover, an article with a canonical URL pointing to will be built as part of my blog whereas, if its canonical URL is it will be a portfolio piece.

For every article that meets the requirements, the subsequent build process is followed:

  1. At build time, Next.js calls the getStaticPaths function which

    1. Fetches a list of my published articles using the API (/api/articles/me).
    2. Converts the article's markdown to HTML.
    3. Saves the articles to a cache file for use in the next step.
    4. A dynamic page is created within the Next.js context for each article - the page slug will be the canonical URL path.
  2. For each page, Next.js calls getStaticProps which fetches the page's article from the cache. The article contains the name, description and HTML.

    • I also attempted making another API request to the API (/api/articles/{id}) to fetch the page's article, so I could use the HTML rendered by However, this caused build failures as I was making too many API requests at once - so now I render the markdown using remark-html.
  3. Finally, the page is rendered. I use custom elements to display the article name and description and then display the HTML I rendered earlier in getStaticPaths using remark-html. For styling, I use the Tailwind Typography plugin.

To ensure that the website is always in sync with my articles on, I use a Vercel Deploy hook which is triggered each time I create or update an article using a webhook. I use a Deploy Hook rather than Incremental Static Regeneration so that the blog is only rebuilt when something has changed rather than at random intervals.

Note: I use APIs that require authorisation as they seem to have a higher request limit compared to the public routes. When using public APIs and fetching each article via the article API, I found that my builds were failing with a 429 error which is rate-limiting requests. - I probably could switch to using public APIs now that I'm using a cache to read the articles from.

I'm currently writing a detailed article which describes in greater detail how my website utilises as a CMS, stay tuned (and follow on to be notified when I release it)!

How it looks


Navigating through my website

Navigating through

Future improvements

  1. Add syntax highlighting to code blocks like on Completed using highlight.js and remark-highlight.js.
  2. Add a contact form using EmailJS.
  3. Only rebuild the website if the content of an article has changed or one is created - reduces the website being needlessly redeployed.


In this article, I discussed rewriting my personal website from the ground up using as a Content Management System for the blog and portfolio pages.

Like the idea of using as a CMS for your blog? React! Found something I could improve or that you would have done differently? Let me know in the comments.

Looking to use the API to power your website or blog? Read my how-to post.

Thanks for reading!

By the way, you can view this article live on my website here.

Top comments (16)

juliang profile image
Julian Garamendy

Hi! I'm doing something very similar.

I'm using disk cache to solve the 429 error "Too many requests".

I would love to know what you think.

jameswallis profile image
James Wallis

Hi Julian! Our solutions are extremely similar. I had a look through your article, good read - I could have used diagrams like yours to make mine a bit clearer!

AFAIK the main difference between yours and mine is that for getStaticProps I'm calling the API that returns the information for a specific article (/articles/:id) whereas you would call get all articles (if not for the cache) (my API call for a single article).

I do this so that I am able to use the pre-rendered HTML that supplies (body_html in the article object), for some reason it isn't included when you query all your published articles - I saw you're parsing the provided article markdown. I wanted to use the supplied HTML so that I didn't need to parse Markdown just to see if it was viable/make the website simpler.

I think if I was to refactor my implementation to make fewer requests I would save the data returned from the get all articles to a cache file in the getStaticPaths file, and in the getStaticProps file read the data directly from the cache rather than querying the API. I already use a cache file to map the page slug to an article ID. The getStaticPaths function writes the cache with a minified array which is then read by the getStaticProps function to find the article ID for a page.

juliang profile image
Julian Garamendy • Edited

Yes. I'm "fetching" all the articles every time (but caching) because I had these two requirements:

  • custom slugs (which you solve with the cache in getStaticPaths)
  • no rebuilds / no redeployments (I can add an article to dev any time and my blog will pick it up without having to regenerate, because it's using Incremental Static Regeneration)
Thread Thread
jameswallis profile image
James Wallis

Nice one, yeah I opted to use the Webhook calling Vercel Deploy hook on an article create/update rather than using Incremental Static Regeneration so that I don’t call the API when no changes have been made. The only downside with the webhook is that even if I edit and save without any changes, a deployment is kicked off.

I think in the near future (perhaps this weekend) I’ll change my website to call the API once to get all the articles, cache them, then convert the cached markdown and display - should solve my ‘429’ error! Thanks for the help

Thread Thread
prnvbirajdar profile image
Pranav Birajdar • Edited

I was just wondering why API has different route options to display same data and you guys answered it for me! I am building my website and it's my first time using getStaticProps and getStaticPaths.

Thank you for clarifying some things for me!

Thread Thread
jameswallis profile image
James Wallis

Happy I could help!

yoursunny profile image
Junxiao Shi

This is a risky direction.
In 2035, is bankrupt and no longer exists. You still have your Markdowns but you lose the uploaded pictures.

I have a few blog posts written around 2008 where images were uploaded to third party hosting services. In 2017, I discovered that the images hosting service was closed and all these images disappeared.
I had to dig out the original files of these blog posts and re-export the images. The files were in Kingsoft WPS 97 format, and I had to download that application in order to open the files.

I'm still mourning the loss of a few videos from 2006 that I uploaded to now-defunct third party hosting and no longer have a backup myself.

jameswallis profile image
James Wallis • Edited

I disagree.
There is nothing wrong or risky in leveraging as a CMS, as long as you back up your data.

It would be pretty trivial to set up a service/app to save your articles each day using the API and performing a download of any images you've used in your articles.

Why not take advantage of the free platform is providing? Just take backups of your data.

foolhardy21 profile image

loved your website!

jameswallis profile image
James Wallis

Thanks Vinay!

abrahamlawson profile image

Also love next.js <3

teamallnighter profile image
Chris Connelly

looks amazing dude! great work

jameswallis profile image
James Wallis


z2lai profile image


judecodes profile image

Hi first time loading the site and it was super fast! Can you do an article of how you did it?

jameswallis profile image
James Wallis

Yes I’m working on one at the moment!