How I Built My Website

May 08, 2022

The tech stack and building process

The technologies I picked to make this website are not too complicated and I've collected them all throughtout the last couple years.

Next.JS

I picked Next.JS for this project as it's super fast, scalable and easy to use. Next.JS's server side rendering also allows me to dynamically generate custom Open Graph images for my blog using Rust.

Tailwind CSS

Tailwind is a great collection of CSS utilities that I use to style my website. It allows me to easily style my website quickly, all without having to write CSS.

Contentlayer

Contentlayer is a newer tool that manages you content for you. It includes hot reload and caching, making it so much better than manually implementing MDX Bundler. On top of that, it allows for validation and automatic Typescript type generation.

Rust

Now the fun part! I used Rust to dynamically generate custom Open Graph images for my blog. After modifiying @code_punkt's library wasm-layout-text to use newer and faster APIs, I was able to make a custom wrapper for it that acted like Contentlayer. Once the thumbnail is generated, I cache it so that hot reloads don't take too long. This is one of the pluses of Next.JS, it allows easy access to the file system unlike some other frameworks.

Some challenges

The hardest part of this website is the dynamic OG image generation. Text layouts are a bit wanky in glyph_brush_layoutglyph_brush_layout for my use case, and dimensions didn't always corespond with what I had on Figma. To try and fix this, I needed to calculate the number of characters per line and then hard code heights for each line.

lib/open-graph.ts
const heights = [113, 160, 210];
 
let lines = Math.ceil(title.length / 55);
if (lines > 3) {
  title = `${title.substring(0, 3 * 55 - 3)}...`;
  lines = 3;
}
 
// { x: 60, y: heights[lines - 1] }
const dateLayout = {
  text: `Samuel Corsi-House - ${date}`,
  fontSize: 38,
  color: '#D1D5DB',
  font: path.join(ogAssets, 'fonts', 'SourceSansPro-SemiBold.ttf'),
  alignment: new Alignment(HorizontalAlign.Left, VerticalAlign.Top),
  bounds: new Dimension(895, 180),
  position: new Position(60, heights[lines - 1])
};
lib/open-graph.ts
const heights = [113, 160, 210];
 
let lines = Math.ceil(title.length / 55);
if (lines > 3) {
  title = `${title.substring(0, 3 * 55 - 3)}...`;
  lines = 3;
}
 
// { x: 60, y: heights[lines - 1] }
const dateLayout = {
  text: `Samuel Corsi-House - ${date}`,
  fontSize: 38,
  color: '#D1D5DB',
  font: path.join(ogAssets, 'fonts', 'SourceSansPro-SemiBold.ttf'),
  alignment: new Alignment(HorizontalAlign.Left, VerticalAlign.Top),
  bounds: new Dimension(895, 180),
  position: new Position(60, heights[lines - 1])
};

The downside to this solution is that if I ever want to change the font size, I would have to recalculate the heights.

What I've learned

Throughout this process I've learned a lot about MDX and how it works. I've also learned more about WebAssembly, the Rust ecosytem, and integrating Rust with Node.JS as WebAssembly.

Future goals

In the future I'd like to further improve the Rust code and try to move more image processing to Rust since it's way faster than Jimp. I'd also like to add Fathom Analytics to the site to track page views because it has a tiny bundle size and it's privacy focused.