···11+---
22+title: How to create a Rehype plugin that turns GitHub links into beautiful badges
33+description: TOD
44+date: 2025-07-12
55+tags:
66+ - Plugins
77+authors:
88+ - trueberryless
99+excerpt: TODO
1010+cover:
1111+ alt: A beautiful cover image with the text "Badge Links"
1212+ image: ../../../../public/blog/rehype-github-badge-links.png
1313+tableOfContents: false
1414+---
1515+1616+import StarlightBadgeLink from "../../../components/StarlightBadgeLink.astro";
1717+1818+Recently, I added a very little neat feature to this blog, which you might have noticed as a regular reader. It's nothing huge, but gives every blog this little lovely touch I, which I have longed for a long time. I am ~talking~ writing about all those green badges with profile pictures you can see in this blog as well.
1919+2020+The inspiration to create them was taken from none other than [Antfu](https://github.com/antfu) himself, as he has those badges all over the place on his website (as time of writing).
2121+2222+Initially I thought they would require some long and tedious programming session to accomplish the same visual beauty. However, after some Vibe Coding - as [Andrej Karpathy coined the term](https://x.com/karpathy/status/1886192184808149383) - I quickly realized that those badges were just a small [rehype](https://github.com/rehypejs) plugin away from existence.
2323+2424+This is the whole code behind the rehype plugin:
2525+2626+```ts
2727+// src/lib/rehype-github-badge-links.ts
2828+import { h } from "hastscript";
2929+import { visit } from "unist-util-visit";
3030+3131+export default function rehypeGitHubBadgeLinks() {
3232+ return (tree) => {
3333+ visit(tree, "element", (node) => {
3434+ if (
3535+ node.tagName === "a" &&
3636+ typeof node.properties?.href === "string" &&
3737+ node.properties.href.startsWith("https://github.com/")
3838+ ) {
3939+ const match = node.properties.href.match(
4040+ /^https:\/\/github\.com\/([\w-]+)\/?$/
4141+ );
4242+ if (match) {
4343+ const username = match[1];
4444+4545+ // Add GitHub badge class
4646+ node.properties.className = (node.properties.className || []).concat(
4747+ "gh-badge"
4848+ );
4949+5050+ // Build avatar image
5151+ const avatarImg = h("img", {
5252+ src: `https://github.com/${username}.png`,
5353+ alt: username,
5454+ width: 16,
5555+ height: 16,
5656+ style:
5757+ "border-radius:9999px;vertical-align:middle;margin-right:0.4em;",
5858+ });
5959+6060+ // Prepend avatar image to original children
6161+ node.children.unshift(avatarImg);
6262+ }
6363+ }
6464+ });
6565+ };
6666+}
6767+```
6868+6969+Basically all this plugin does, is walking through the HTML, looking for links which reference to any GitHub profile. If it finds one, it adds an `<img>` tag before the text content with the profile picture of the GitHub user or organisation. This is possible very consistently thanks to GitHub's feature of making the picture available as a resource behind the profile link appended with `.png`. Read more about that feature in [this awesome article on `dev.to`](https://dev.to/10xlearner/how-to-get-the-profile-picture-of-a-github-account-1d82).
7070+7171+With a little bit of additional styling it looks really cute in my opinion. Please note that since this is a <StarlightBadgeLink /> page, I use available CSS variables, like in line four:
7272+7373+```css "var(--sl-color-accent-low)" "var(--sl-color-accent)"
7474+// src/styles/custom.css
7575+.gh-badge {
7676+ display: inline-flex;
7777+ align-items: center;
7878+ background-color: var(--sl-color-accent-low);
7979+ border-radius: 9999px;
8080+ padding: 0em 0.5em 0 0.3em;
8181+ font-size: 0.9em;
8282+ text-decoration: none;
8383+ color: inherit;
8484+ font-weight: 500;
8585+ transition: background-color 0.2s ease;
8686+ transform: translateY(0.29rem);
8787+ border: 1px solid var(--sl-color-accent);
8888+}
8989+9090+.gh-badge:hover {
9191+ background-color: var(--sl-color-accent);
9292+}
9393+9494+.gh-badge img {
9595+ border-radius: 9999px;
9696+ width: 1.3em;
9797+ height: 1.3em;
9898+}
9999+```
100100+101101+Now to put everything together, let's say for example in an Astro site, you just need to add the rehype plugin to the configuration like this:
102102+103103+```ts ins={6-8}
104104+// astro.config.mjs
105105+import { defineConfig } from 'astro/config';
106106+import rehypeGitHubBadgeLinks from "./src/lib/rehype-github-badge-links";
107107+108108+export default defineConfig({
109109+ markdown: {
110110+ rehypePlugins: [rehypeGitHubBadgeLinks]
111111+ }
112112+})
113113+```
114114+115115+Read more about the injection of rehype plugins in Astro in [their configuration reference](https://docs.astro.build/en/reference/configuration-reference/#markdownrehypeplugins).
116116+117117+Do not forget to add the CSS in a similar way depending on your framework - in Starlight you can configure custom global CSS styles following [these instructions](https://docs.astro.build/en/guides/styling/#scoped-styles) - and you can admire your own badge links too. Feel free to share this post with anyone you want to persuade to use these features too.
118118+119119+<details>
120120+<summary>✨ Bones point</summary>
121121+122122+If you want to use such a badge for other links too, I recommend that you craft yourself a little Astro component like this one:
123123+124124+```astro
125125+---
126126+// src/components/BadgeLink.astro
127127+const { href, src, text, className = "gh-badge" } = Astro.props;
128128+---
129129+130130+<a href={href} class={className}>
131131+ <img src={src} alt={text} />
132132+ {text}
133133+</a>
134134+```
135135+136136+Just make sure that the CSS is globally (or [scope](https://docs.astro.build/en/guides/styling/#scoped-styles) it in the component above) and it's ready to be used:
137137+138138+```mdx
139139+import BadgeLink from "../components/BadgeLink.astro";
140140+141141+<BadgeLink
142142+ href="https://github.com/withastro/starlight"
143143+ src="/starlight.png"
144144+ text="Starlight"
145145+/>
146146+```
147147+148148+</details>