GitHub Stats Dashboard: Real Data, Real Time

#Next.js#GitHub API#Data Visualization#React

GitHub Stats Dashboard: Real Data, Real Time

When building a portfolio, showing what you do is one thing. Showing how active you are is another level entirely.

I wanted my About page to have more than just a static list of skills. I wanted it to breathe with real data from GitHub — contributions, stars, and a beautiful contribution heatmap.

The Challenge

GitHub's public API is powerful, but it has quirks:

  • The Events API often returns PushEvent entries without commit data (commits: 0)
  • Rate limits are strict: 60 requests/hour without auth, 5,000 with a token
  • The Contributions API isn't official — I had to use github-contributions-api.jogruber.de

The Solution

1. Fetching the Data

I created a GithubStats.tsx component that fetches from three sources:

// GitHub user profile (for public repos count)
const profileRes = await fetch("https://api.github.com/users/rafaelsanoli");

// All repos (to calculate total stars)
const reposRes = await fetch("https://api.github.com/users/rafaelsanoli/repos?per_page=100");
const totalStars = reposData.reduce((acc, repo) => acc + repo.stargazers_count, 0);

// Contributions heatmap data
const contribRes = await fetch("https://github-contributions-api.jogruber.de/v4/rafaelsanoli?y=last");

2. The Contribution Graph

The most visually striking part is the contribution heatmap. I built a separate ContributionGraph.tsx component that:

  • Breaks down 365 days into weeks (7-day chunks)
  • Maps contribution levels (0-4) to neon cyan opacity and glow shadows
  • Uses Tailwind's box-shadow for that cyberpunk aesthetic
const getLevelColor = (level: number) => {
    switch (level) {
        case 0: return "bg-white/5";
        case 1: return "bg-[#00f3ff]/20 shadow-[0_0_5px_rgba(0,243,255,0.2)]";
        case 2: return "bg-[#00f3ff]/40 shadow-[0_0_8px_rgba(0,243,255,0.4)]";
        case 3: return "bg-[#00f3ff]/60 shadow-[0_0_12px_rgba(0,243,255,0.6)]";
        case 4: return "bg-[#00f3ff] shadow-[0_0_15px_rgba(0,243,255,0.8)]";
        default: return "bg-white/5";
    }
};

3. The Bug That Broke Everything

Initially, I tried to display Latest Activity by fetching commit messages from the Events API. But I hit a wall:

{
  "type": "PushEvent",
  "repo": "rafaelsanoli/modula",
  "commits": 0,  // ❌ Empty!
  "first_commit": null
}

GitHub's API sometimes returns PushEvents without commit details. The fix? I tried fetching commits directly from the most recently updated repos, but ultimately decided to remove the feature because:

  • It wasn't reliable
  • Real-time commit tracking requires webhooks (impossible on a static site)
  • The heatmap already shows activity trends

What I Learned

  1. Error Handling is King: Always add ?. optional chaining when dealing with APIs
  2. Defensive Programming: Filter out bad data before mapping:
    eventsData.filter(event => event.type === "PushEvent" && event.payload?.commits?.length > 0)
    
  3. Know When to Quit: Sometimes removing a feature is better than shipping a broken one

The Result

Now my About page displays:

  • 15 Public Repos
  • 3 Total Stars (and counting!)
  • 222 Contributions in the last year
  • A glowing, interactive Contribution Heatmap

All fetched in real-time, every time someone visits the page.

Try It Yourself

Check out the live dashboard on my About page, or explore the code on GitHub.


Tech Stack: Next.js, TypeScript, Tailwind CSS, GitHub API

SELECT LANGUAGE / SELECIONE O IDIOMA