An animated, self-hosted GitHub-style contribution calendar card for your profile README.
Continuously scrolling, pixel-matched to GitHub's own contribution graph, light or dark. Plain JavaScript, deployed as Vercel Serverless Functions — no framework, no build step, no database.
- Features
- Examples
- Quick Start
- Creating a GitHub Personal Access Token
- Deployment
- Usage
- Local development
- Troubleshooting
- License
- 🟩 Matches GitHub's contribution colors (light & dark)
- 🔁 Infinite scroll animation — single animated SVG, no GIF, no client JS
- 🌗 Light/dark theme via
?theme= - 🔒 Username allowlist via
USERNAMES, so your token can't be used to scrape arbitrary accounts - 📆 Full history by default, or from a custom start year/date via
?year=/?date= - 🎨 Custom text/background/border colors via
?color=,?bg-color=,?border=— the grid squares stay fixed - 📱 Responsive width, edge-cached data
`https://github-profile-calendar-graph.vercel.app/api/calendar?user=MrDeveloperJIS&theme=light``https://github-profile-calendar-graph.vercel.app/api/calendar?user=MrDeveloperJIS&theme=dark``https://github-profile-calendar-graph.vercel.app/api/calendar?user=MrDeveloperJIS&theme=dark&year=2020``https://github-profile-calendar-graph.vercel.app/api/calendar?user=MrDeveloperJIS&theme=dark&date=20092024``https://github-profile-calendar-graph.vercel.app/api/calendar?user=MrDeveloperJIS&theme=dark&year=2025&color=f0f8ff&bg-color=0c0c0c&border=f0f8ff`Full parameter list and defaults: Usage.
- Fork this repo.
- Create a GitHub Personal Access Token — see guide below.
- Deploy to Vercel and set two environment variables — see Deployment.
- Embed the image URL in your profile README — see Usage.
Needs a classic token with only the read:user scope — fine-grained PATs don't reliably support the GraphQL field this project relies on.
Step-by-step guide (click to expand)
https://github.com/settings/tokens→ Generate new token → Generate new token (classic).- Name it (e.g.
github-profile-calendar-graph), set an expiration. - Under Select scopes, check only
read:user. - Generate token and copy it immediately — GitHub only shows it once.
- Don't commit it to the repo — it goes into Vercel's environment variables only (see Deployment), used server-side, never exposed to viewers of your README.
A user's private contributions only show if they've enabled "Include private contributions on my profile" — same rule as GitHub's own profile page. Token expired? Generate a new one, update
GH_TOKENin Vercel, redeploy.
-
Import your forked repo at Vercel — no build settings needed.
-
Set environment variables under Project → Settings → Environment Variables:
Variable Example Description GH_TOKENghp_xxxxxxxxxxxxxxxxxxxxClassic PAT, read:userscope onlyUSERNAMESoctocatoroctocat,mona,hubotComma-separated allowlist. No spaces. -
Deploy, then test in a browser:
https://your-project.vercel.app/api/calendar?user=YOUR_USERNAME
See it rendered: Examples.
| Param | Default | Example | Description |
|---|---|---|---|
user |
— | ?user=octocat |
Required. Must be in the USERNAMES allowlist |
theme |
light |
?theme=dark |
light or dark |
year |
full history | ?year=2023 |
Jan 1 of that year → today |
date |
— | ?date=15022025 |
Exact start date, ddmmyyyy → today. More specific than year; wins if both are given |
width |
512 |
?width=760 |
Card width in px. Clamped 200–2000 |
border-radius |
0 |
?border-radius=12 |
Corner radius, px. Clamped 0–50 (was radius) |
border-width |
1 |
?border-width=2 |
Border width, px, 0 = none. Clamped 0–10 (was border) |
speed |
40 |
?speed=60 |
Scroll speed, px/sec. Clamped 5–300 |
color |
theme default | ?color=ff8800 |
Text color (labels) |
bg-color |
theme default | ?bg-color=1a1a2e |
Background color |
border |
theme default | ?border=ff8800 |
Border color (no effect if border-width=0) |
Hex values have no leading # (e.g. ff8800). Invalid numeric params return an error card; invalid colors just fall back to the theme default. An invalid date (bad format, or a calendar date that doesn't exist, e.g. 30022026) also returns an error card.
Auto light/dark switching (optional, click to expand)
GitHub also supports switching by viewer color scheme via <picture>:
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://your-project.vercel.app/api/calendar?user=YOUR_USERNAME&theme=dark">
<source media="(prefers-color-scheme: light)" srcset="https://your-project.vercel.app/api/calendar?user=YOUR_USERNAME&theme=light">
<img alt="contribution calendar" src="https://your-project.vercel.app/api/calendar?user=YOUR_USERNAME&theme=light">
</picture>cp .env.example .env
# fill in GH_TOKEN and USERNAMES
node dev-server.js
# or: npm startVisit http://localhost:9000/api/calendar?user=YOUR_USERNAME. dev-server.js runs the exact same api/calendar.js handler as production, using only Node's built-in http module — no CLI or dependencies needed.
Prefer the Vercel CLI instead? npm install -g vercel && vercel dev (or npm run dev once installed) reads the same .env and is closer to Vercel's real runtime — don't run both at once, they share port 9000.
Common issues (click to expand)
| Symptom | Likely cause |
|---|---|
| "Deploy your own, or add this username..." | Username not in USERNAMES, or typo/case mismatch |
| Image doesn't load (broken icon) | Wrong deployment URL, or function errored — check Vercel logs |
fetch is not defined in function logs |
Node.js version < 18 — package.json pins >=18; check Project → Settings → General → Node.js Version |
| Contribution counts look low | User hasn't enabled "include private contributions" |
| Token stopped working | PAT expired — regenerate and update GH_TOKEN |
MIT — Md. Jahidul Islam Sujan. Inspired by GitHub's own contribution graph; not affiliated with or endorsed by GitHub.