Skip to content
Rezha Julio
Go back

Adding a Links Section to My Blog

3 min read

I keep finding cool stuff on the internet. Articles, tools, random projects.

The problem? Most of them don’t deserve a full blog post. But I still want to share them somewhere. Twitter feels too ephemeral. A blog post feels too heavy.

Then I saw Robb Knight’s links page. Perfect. A micro-blog for external content with my own hot takes attached.

So I built one. Here’s how.

The Schema

Every link is just a markdown file with a URL field. The body is my commentary.

const linksSchema = ({ image }: { image: () => z.ZodType }) =>
baseSchema({ image }).extend({
url: z.string().url(),
commentaryPrefix: z.string().optional(),
});

A link file looks like this:

---
title: "Some Interesting Article"
pubDatetime: 2026-01-25T10:00:00+07:00
url: "https://example.com/article"
tags:
- Web
---
My thoughts on this article go here.

Simple. The markdown body becomes my take on whatever I’m sharing.

The Card Component

Each link shows up as a card. Title links out (with a little ↗ arrow), date links to the detail page, and my commentary renders below.

<li class="my-6">
<a href={url} target="_blank" rel="noopener noreferrer">
<span>{title}</span>
<svg><!-- arrow icon --></svg>
</a>
<a href={`/links/${id}/`}>
<Datetime {...datetimeProps} />
</a>
<div class="prose">
<Content />
</div>
</li>

The render() function from astro:content does the heavy lifting, turning markdown into a component I can just drop in.

Why Detail Pages?

I could’ve just made links a flat list. But I wanted each link to have its own URL. Why?

  1. Shareable. I can send someone /links/cool-article/ instead of “check my links page and scroll down.”
  2. Comments. Each link can have its own Mastodon thread.
  3. Navigation. Previous/next buttons let you browse through links like posts.

OG Images

This is the fun part.

I wanted links to look different when shared. The OG image shows a [LINK] badge, the title, and the domain extracted from the URL.

function extractDomain(url) {
try {
return new URL(url).hostname.replace(/^www\./, "");
} catch {
return "";
}
}

There’s also a commentaryPrefix field. Defaults to “Rezha on” but I can customize it per link. So when someone shares my link on social media, it says “Rezha on Some Interesting Article” with the source domain below.

The OG images generate at build time:

export const GET: APIRoute = async ({ props }) => {
const buffer = await generateOgImageForLink(props);
return new Response(new Uint8Array(buffer), {
headers: { "Content-Type": "image/png" },
});
};

Here’s an example

Scaffolding

I already had bun run new for posts and notes. Added links to the mix.

Terminal window
$ bun run new
? What do you want to create? link
? URL: › https://example.com/article
? Title: › Some Interesting Article
Created: src/data/links/some-interesting-article.md

It grabs the URL, asks for a title, and generates the frontmatter. I just fill in my commentary and publish.

The Result

Total time: a weekend. Most of it was fiddling with the OG image layout.

Is it overkill for sharing links? Maybe. But now I have a place for all those “this is cool but not blog-post-worthy” finds.

Check it out at /links/.


Share this post on:

Related Posts


Next Post
Pair Programming with a Lobster: My Week with Clawdbot