Changelog of tombates.co

This year, I set out to learn and build things in public, what you see below is a log of all design and development decisions during the continued process of creating for this site. Sometimes, finding something to write about can be tricky, and this gives me an easy go-to subject when I lack inspiration.

Tracking page views with Fathom and Next.js

Everyone likes to see metrics, even if it's just vanity. It might not be real validation of my writing, but it's nice to know at least people are visiting my tiny corner of the web. After reading, Damian Bradfield's book, The Trust Manifesto, about big data and privacy it got me thinking. How much do I need to track and know about my audience?

I knew I was going to need a solution to help me track visitors but which one? My search began with the usual suspects such as Google, Heap, and Mixpanel. They're all great products, but they follow you around a lot. I thought all was lost until I came across FathomFathom is a simple and private website analytics platform. I started reading a few of their posts and found their post describing how they handle anonymisation particularly interesting. Their views on privacy, coupled with the fact that they are a small company, and I was sold.

At first, setting up Fathom was as simple as adding their snippet. However, once I pushed my site live, I noticed page views weren't getting tracked accurately. Server renders got logged correctly, but page changes on the client were missing (unless no-one was browsing more than one page). The problem was pretty obvious, Fathom's snippet didn't account for single-page applications, and the solution on their site didn't seem to work with Next.js.

The solution was pretty simple, use Fathom's snippet to log all server renders from _document.js, and track all subsequent clientside route changes in _app.js via Next.js's router events.

// _document.js
import Document, {
  Html,
  Head,
  Main,
  NextScript
} from "next/document";

const __html = `(function(f, a, t, h, o, m){
  a[h]=a[h]||function(){
  (a[h].q=a[h].q||[]).push(arguments)
  };
  o=f.createElement('script'),
  m=f.getElementsByTagName('script')[0];
  o.async=1; o.src=t; o.id='fathom-script';    
  m.parentNode.insertBefore(o,m)
  })(document, window, 'https://cdn.usefathom.com/tracker.js', 'fathom');
  fathom('set', 'siteId', '<YOUR_TRACKING_CODE>');  
  fathom("trackPageview");`;

export default class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head />
        <body>
          <Main />
          <NextScript />
          <script dangerouslySetInnerHTML={{ __html }} />        
        </body>
      </Html>
    )
  }
}

// _app.js
import Router from 'next/router';

const trackPageView = () => {
  if (typeof window !== "undefined" && typeof window.fathom !== "undefined") {
    window.fathom("trackPageview");
  }
};

Router.events.on("routeChangeComplete", trackPageView);

export default ({ Component, pageProps }) => {
  return <Component {...pageProps} />
}

And that's it.

Adding individual pages with SWR

As I add more content to the site, I'd like to be able to share direct links to posts. It's a pretty simple change to add individual changelog and blog pages. To start, I first had to decide on the URL structure, which had a natural choice, following best practice. I ended up with the following: https://tombates.co/{resource}/{slug}. With Next.js, all it takes to create dynamic routing is to create a new file inside the pages directory. For my changelog articles, the path looks like pages/changelog/[slug].tsx.

Then it's just a case of hooking up the data. Below is the skeleton for the page minus the details.

import React from "react";

const fetchData = async slug => {
  return Promise.resolve({ title: slug });
}

const ChangelogPage = ({ article }) => {
   if (!article) {
    return (<div>Article not found!</div>);
  }

  return (<article>{article.title}</article>);
};

ChangelogPage.getInitialProps = async ({ query }) => {
  const article = await fetchData(query.slug);
  return {
    article
  };
};

export default ChangelogPage;

Once I had the primary setup for the page in place, I wanted to look at using a brilliant library from Zeit called SWR, even if it's a little bit of overengineering. SWR is a React Hooks library for remote data fetching. It has a bunch of exciting features, including caching and fast page navigation. However, the feature I was mostly interested in was Revalidation on focus, which would allow me to edit my content on Contentful and instantly see updates when returning to my site.

Getting SWR setup is a piece of cake. All you need is one function call and to hook up the returned data.

import React from "react";
import useSWR from "swr";

const fetchData = async slug => {
  return Promise.resolve({ title: slug });
}

const ChangelogPage = ({ article, slug }) => {
  const { data } = useSWR(slug, fetchData, {
    initialData: article
  });

  if (!data) {
    return (<div>Article not found!</div>);
  }

  return (<article>{data.title}</article>);
};

ChangelogPage.getInitialProps = async ({ query }) => {
  const article = await fetchData(query.slug);

  return {
    article,
    slug: query.slug
  };
};

export default ChangelogPage;

And that's it. We're all setup with individual pages with almost live updates using SWR. You can find more examples of SWR here.

Building the foundations

When you're building something, you want to start with strong foundations, but in this case, it wasn't my focus. I wasn't so focused on building time proof foundations for a personal website. Instead, I focused on creating something easy to use with little overhead.

Initially, I wanted to use little to no javascript because a simple blog really doesn't need it. I thought about using a framework like JekyllMiddleman, or 11ty, something bare-bones that outputs a few files that I can serve from anywhere. However, in the end, I opted for my usual go-to stack of Next.jsTypeScript, and Now. I like to avoid the overengineering that usually comes with frontend development (if you're not careful) and Next.js makes that rediculously easy.

I'm not losing any of the speed statically serving gives you because Next.js allows me to render a React application directly from the server. No loading indicators or waiting for scripts to execute. For the most part, it's the same experience for the person reading, with or without javascript. There's a lot of other great things about Next.js including fantastic documentation and heaps and heaps of examples, but I'm not going to list them all here.

So, once I had the base of the foundations, the next part for figuring out the content process. To start, I wanted to make it as simple as possible and set out to use MDX (React flavoured Markdown), allowing me to create a simple Markdown file for each of my blog posts. However, this meant that each time I wanted to post a new article, I would need to add it to the repo on Github. Not ideal if I wanted to be able to post on the fly. I was going to need something a little more dynamic.

Enter Contentful, an API-first Content Management System. Contentful allows me to create all my content in one place and fetch it from my site regardless of the technology used. There are a few similar products available, one being Prismic. I honestly didn't spend any time evaluating the best product to use and chose the one that was more or less familiar to me.

With all the decisions made, I was able to pull together the initial version of the site in under a day and have my first two blog posts live. I used a couple of other interesting libraries and learnt a few things along the way, but I'd like to write about them technically on their own.