# github-stats — Custom SVG GitHub Widgets on Cloudflare Workers

I built a self-hosted Cloudflare Worker that generates dynamic SVG widgets from the GitHub API — with a full theming system, dark/light mode, KV caching, and transparent backgrounds for embedding anywhere.

**Canonical:** https://spoko.space/blog/github-stats/  
**Language:** en  
**Published:** 2026-06-14  
**Tags:** cloudflare-workers, typescript, svg, github-api, open-source, kv-cache, simple-icons  
**Category:** Engineering

---
<div class="text-center flex gap-4 justify-center flex-wrap">

</div>

## The problem

Every developer's GitHub profile tells a story — commits, streaks, top languages, contribution heatmaps. But existing widget services come with limitations: fixed themes that clash with your design, no control over caching, rate-limit issues, and no way to match your portfolio's color palette.

I wanted widgets that:
- match the dark blue palette of spoko.space exactly
- switch between dark and light theme automatically via `prefers-color-scheme`
- work without a background so they embed seamlessly into any page
- are cached at the edge so they're fast and don't hammer the GitHub API

So I built one.

## The result

<div class="flex flex-col gap-4 items-center my-8">
  <picture class="w-full max-w-2xl">
    <source media="(prefers-color-scheme: light)" srcset="https://github.spoko.space/profile?theme=light&v=1" />
    <img src="https://github.spoko.space/profile?v=1" alt="GitHub Profile Stats" width="460" height="200" loading="lazy" class="w-full h-auto rounded-lg" />
  </picture>
  <picture class="w-full max-w-2xl">
    <source media="(prefers-color-scheme: light)" srcset="https://github.spoko.space/langs?theme=light&v=1" />
    <img src="https://github.spoko.space/langs?v=1" alt="Most Used Languages" width="460" height="200" loading="lazy" class="w-full h-auto rounded-lg" />
  </picture>
  <picture class="w-full max-w-2xl">
    <source media="(prefers-color-scheme: light)" srcset="https://github.spoko.space/contrib?theme=light&v=1" />
    <img src="https://github.spoko.space/contrib?v=1" alt="Contribution Calendar" width="460" height="133" loading="lazy" class="w-full h-auto rounded-lg" />
  </picture>
</div>

These are live widgets — rendered in real time from my GitHub data, cached at Cloudflare's edge, and switching theme automatically based on your system preference.

## Architecture

The service runs as a **Cloudflare Pages** project — a serverless function deployed at the edge, close to the visitor. The stack is intentionally minimal:

- **TypeScript** — type-safe rendering pipeline with no rendering framework
- **Cloudflare KV** — edge key-value store caches API responses for 24 hours, so the GitHub API is hit at most once per day per data type
- **GitHub GraphQL API** — a single query fetches contributions, streak data, and repository stats
- **Pure SVG generation** — no canvas, no headless browser, no image libraries; just string templates producing valid SVG

SVG was a deliberate choice: it's pixel-perfect on any display (retina included), compresses to a few kilobytes, and lets you change colors purely through URL parameters — no server-side image processing needed.

The project handles nine endpoints (`/langs`, `/stats`, `/streak`, `/contrib`, `/repos`, `/trophies`, `/stack`, `/profile`, `/icon`) and returns `image/svg+xml` responses with proper `Cache-Control` headers.

## Theming system

Every endpoint accepts URL query params to override colors:

```
/langs?theme=light
/langs?bg=0d1117&primary=58a6ff&text=c9d1d9&radius=6
/profile?bg=transparent&border=transparent
```

Ten built-in themes ship out of the box: `dark`, `light`, `tokyonight`, `dracula`, `github-dark`, `nord`, `radical`, `synthwave`, `catppuccin`, `gruvbox`, `aura`, `discord`. The `transparent` background support — added specifically for portfolio embeds — lets widgets blend into any page regardless of its background color.

For GitHub README embeds, the `<picture>` element handles dark/light switching automatically:

```html
<picture>
  <source media="(prefers-color-scheme: light)"
    srcset="https://github.spoko.space/langs?theme=light">
  <img src="https://github.spoko.space/langs" alt="Languages">
</picture>
```

## Tech stack icons via simple-icons

The `/stack` widget pulls icon paths and brand colors directly from the [simple-icons](https://simpleicons.org/) npm package — no manual SVG copying, no stale colors. With Cloudflare's esbuild bundler and tree-shaking, only the imported icons end up in the bundle.

<div class="my-6 flex justify-center">
  <picture class="w-full max-w-2xl">
    <source media="(prefers-color-scheme: light)" srcset="https://github.spoko.space/stack?techs=Vue%2CAstro%2CTypeScript%2CTailwind%2CPHP%2CLaravel%2CNode.js%2CWordPress&theme=light&v=1" />
    <img src="https://github.spoko.space/stack?techs=Vue%2CAstro%2CTypeScript%2CTailwind%2CPHP%2CLaravel%2CNode.js%2CWordPress&v=1" alt="Tech Stack" width="460" height="60" loading="lazy" class="w-full h-auto rounded-lg" />
  </picture>
</div>

## Fork & deploy

The project is open source under MIT. Deploying your own instance takes about five minutes:

```bash
git clone https://github.com/spokospace/github-stats
cd github-stats
npm install
npx wrangler secret put GITHUB_TOKEN
npx wrangler secret put CACHE_BUST_TOKEN
npx wrangler pages deploy .
```

Update the `OWNERS` array in `src/index.ts` with your GitHub username(s) and you're done. Full docs at [github.spoko.space](https://github.spoko.space).
