cartoon face
This Website

Welcome to my website 😊 I created it to address some problems I had been having:

  • Aside from GitHub, I did not have a portfolio to show my skills, notable projects, or ways to contact me
  • I didn't have anywhere to deploy, access, and test out different ideas or projects in a centralised manner
  • My online presence as a developer was nonexistent

This was also a good opportunity for me to reinforce concepts of web design and development and learn new things. Of course, it's easy enough to use a service such as Squarespace to create a website or simply use GitHub as a portfolio, but I wanted to stay away from 'cookie-cutter' approaches as they did not feel as personal.

I wanted this website to be clean, minimal and straight to the point: people who visit usually want to know what I've done and what I'm able to do as a developer without any superfluous information. For these reasons, I opted for a single scrolling page with just the information relevant to my portfolio. I decided to give every project its own page where I could discuss them in further detail for anyone interested to know more.

I could have also achieved this site with a primitive stack, but I decided to overcomplicate it for the sake of learning and some future-proofing: keep reading to find out exactly how I went about making this website, including some of the problems I had and what I did to overcome them!


I wanted to be able to upload, edit and delete projects without touching any code - so I decoupled the backend and frontend by using a headless CMS. I opted for Strapi because it had some good documentation and was completely open-source, meaning there was plenty of community support and most importantly: it's free! Strapi allows me to upload, edit and delete projects through an online panel which is very similar to WordPress. This way, I'm able to add new projects in the future and delete them from practically any device with an internet connection and a browser. It serves this data through REST API endpoints, which are fetched by the frontend to populate the site with data.

One important thing to note is that using the default media library can cause problems where it is wiped on every deployment. To deal with this I changed the default media uploader to Cloudinary. Integrating it into the Strapi admin panel was as easy as installing strapi-provider-upload-cloudinary, writing the config, and adding in my environment variables. No further problems after this!

Using Strapi in the development phase was extremely easy, with hardly any issues setting it up. It's probably because my use case for this project was very simple, and so not much configuration was needed - just a simple project Content-Type with the relevant fields for data.


This was an easy choice for the frontend. Next.js is a React framework that allows for static site generation and comes bundled with extra features like routing and image optimisation. React is becoming my comfort zone as time goes on, and it only felt right to use it for the frontend of my portfolio site. Data fetching is also made very simple using functions like GetStaticProps, which in conjunction with dynamic routing made having unique pages for projects very simple. As the site is statically generated, it is built upon deployment into HTML which makes for an extremely smooth SEO experience as well as being incredibly fast.

Other than that, everything else could have been achieved in any other framework just as easily. Everything lives in its own component or set of components - such as multiple ProjectCard components in a ProjectGrid, or Icon components in a Nav.

One problem I encountered was styling not loading immediately. Instead, unstyled text would be rendered first before firing off transitions to what the text should actually look like. This can be seen in the gif below (turn your brightness up):


This turned out to be a bug with Chrome itself, and after finding an odd but working solution on Stack Overflow, I was able to implement it into a _document.js file like so:

import Document, { Html, Head, Main, NextScript } from "next/document"; export default class MyDocument extends Document { render() { return ( <Html lang="en"> <Head> <meta charSet="utf-8" /> </Head> <body> <Main /> <NextScript /> <script> </script> </body> </Html> ); } }

Everything worked fine after that thankfully!


For the styling, I decided to try out using Tailwind CSS. Normally, I would opt for something like styled-components for a project of this size, but I decided to give it a try after seeing some people praise how fast and easy it was. I know what you're thinking - "Styling inline HTML is ugly and unmaintainable", and you're partly right. It's pretty ugly, but once a system of ordering ClassName values is set along with extending the original config, it became pretty maintainable (providing everything is sensibly compartmentalised into respective components). I was naive at first, so it ended up being pretty messy - but after some refactoring, I ended up with a solid system to layout my styles. It's also worth noting that Tailwind has some of the best documentation I've encountered for a long while. I'm not sure if going back to vanilla-style CSS will ever be the same. 😅


For the backend, I hosted Strapi on Heroku using the free hobby plan along with PostgreSQL to store the data. I ran into quite a few errors with deployment here, due to Heroku outputting some weird errors about SSL but these were fixed quickly, as it seemed to be a common problem! However, compared to other CMS options like Forestry, there was definitely a lack of easy deployment options.

With the frontend, I deployed my Next.js project on Vercel. This was probably the quickest deployment process I've encountered so far - mainly because the framework is created by Vercel, and it's clear they've optimised the deployment platform for it.

Both parts of the site have automatic deployment based on GitHub repositories, meaning that whenever I push changes to main they immediately begin to rebuild and deploy. On top of that, I used a webhook between Strapi and Vercel which triggers my frontend to be rebuilt every time there is a change in my CMS.

Overall, this was a pretty fun project to get back into web development with! I still have a lot to learn, but I'm excited to move further into some interesting projects.

</> with ♥
Jon Linkens 2022