Website rebuild with Eleventy (11ty), Tailwind CSS, and GitHub Issues as CMS

• • #jamstack

I rebuilt my website, so it's time for yet another "How I built my blog" post.

TLDR: The site is built with Eleventy and Tailwind CSS, and GitHub Issues are used to store the blog posts.

This is what I used to build the current version of the website:


Not for all blog posts, but in general, the Lighthouse score is pretty good😊.

Lighthouse score

Why Eleventy?

Eleventy calls itself "a simpler static site generator" on its website. And it is indeed simple and straightforward. One thing I really like about it is that I could easily understand what's going on under the hood and how I can achieve certain steps. This made development a breeze, especially combined with Tailwind CSS for styling.

GitHub Issues CMS

All posts are stored as GitHub Issues (in Markdown format), then the GitHub API is used to fetch all issues, and then Eleventy builds the site with all posts.

GitHub Issues

Thank you to Swyx for this idea.

What's cool about using GitHub Issues as CMS:

  • It's free
  • Easy and fast to update
  • Easily store images - You can attach files by dragging & dropping, selecting, or even pasting from the clipboard.
  • Editable from mobile
  • Publishing/Unpublishing articles is simply done by adding a label called "published"
  • Not implemented here but Issue comments & reactions (👍🎉🚀) can be used as a commenting system

With this short snippet, we can then fetch all issues with the label "published" from GitHub:

const { Octokit } = require("@octokit/rest");

const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,

async function getIssues() {
const { data } = await octokit.issues.listForRepo({
labels: "published",
per_page: 100,
state: "all"

// TODO: add pagination for > 100 posts
return data;

GitHub Actions to trigger build hook

Netlify creates a new build whenever the main branch is updated. But we can also create a new build whenever we edit an Issue by using a GitHub Action. Since I'm using Netlify for deployment, all I have to do is send a post request to the Netlify build hook.

(If you don't have the option with a build hook, you could also use an Action to create a new empty commit to trigger a new build.)

What's really cool is that GitHub Actions can be used to run whenever issues are opened, edited, deleted, labeled, or unlabeled. So every time I make an edit, the new site is live just a few seconds later.

This is what my .github/workflows/ci.ymlfile looks like. Notice the if-statement to skip this Action when I edit an unpublished post:

# Trigger Netlify build hook when GitHub issues change
name: Issue Trigger

types: [opened, edited, deleted, labeled, unlabeled]

# Run if type is either deleted/labeled/unlabeled, or if opened/edited and it is published (we have 1 label)
# No need to run if edited/opened and not yet published
if: (github.event.action != 'edited' && github.event.action != 'opened') || github.event.issue.labels[0] != null
runs-on: ubuntu-latest
- name: REST API with curl
run: |
curl -X POST -d '{}' "$NETLIFY_BUILD_HOOK"

Resources and Credits

Starter Repos with a GitHub CMS

Currently, my site is not Open Source, since Swyx mentioned there can be SEO problems because the content will be duplicated across the GitHub issue and the blog. But maybe I'll release a separate starter template in the future.

Other Starter Repos you can use:

Hope you enjoyed the read!

Share on Twitter • RSS Feed