Skip to content

Repozine: your repository is already a blog

#46

You are reading a GitHub Discussion. I wrote it in the zeikar/repozine repository, pressed Start discussion, and about a minute later it was a page on this blog, with its own URL, a table of contents, search and comments.

That is all Repozine does: it turns a repository's Discussions or Issues into a static blog on GitHub Pages.

Why write in a repository

GitHub already has most of what a blog editor needs, and you are probably using it every day.

  • A Markdown editor with preview, drag-and-drop images and code blocks
  • Labels, which become tags
  • Comments and reactions from people who already have an account
  • Nothing to host, no database and no admin page to secure

What it lacks is a page that reads like a blog instead of a tracker. Repozine builds that page.

How a post gets published

  1. You open a discussion in the category you configured, or an issue in issues mode.
  2. The event starts a GitHub Actions workflow in the same repository.
  3. At build time, Astro fetches your posts through the GitHub GraphQL API and renders their Markdown into plain HTML.
  4. The result is deployed to GitHub Pages.

Editing, relabeling or deleting a post, and new comments, rebuild the site the same way. Only posts written by the repository owner are published, so a stranger opening an issue can't put anything on your blog.

What every site gets

  • A real URL for every post, and a page for every label
  • Full-text search that runs in the browser, powered by Pagefind
  • A table of contents that follows along as you read
  • Syntax highlighting at build time, in light and dark themes
  • sitemap.xml, rss.xml, Open Graph cards and article metadata for search engines and link previews
  • Comments through giscus in discussions mode, or utterances in issues mode
  • Dates shown in the reader's time zone

Here is a code block, so you can see the highlighting:

const tagSlug = (name: string): string => {
  const slug = name.toLowerCase().replace(/[\s/?#%\\]/g, "-");
  return slug === "." || slug === ".." ? slug.replaceAll(".", "-") : slug;
};

Discussions or issues?

Both work, and switching is one line in config.json.

DiscussionsIssues
Where posts liveOne discussion categoryOpen issues
Who can start a postMaintainers only, with an Announcement-format categoryAnyone, but only yours are published
Commentsgiscusutterances
A closed post leaves the siteOn the next buildClosing it rebuilds the site

This blog runs in discussions mode, because an Announcement-format category keeps the post list clean and leaves issues free for actual issues.

Start your own

Create a repository from the template and fill in config.json:

{
  "websiteTitle": "My Notes",
  "repoOwner": "your-github-id",
  "repoName": "your-repo",
  "source": "discussions",
  "discussionCategory": "posts",
  "googleAnalyticsId": "",
  "language": "en"
}

Then set the Pages source to GitHub Actions and push. The README covers adding Repozine to an existing repository, comments and search engines.

If you want to see a site with more posts, LeetCode Study publishes more than 150 study notes from its issues.