Home >Web Front-end >JS Tutorial >How to Add GitHub Contribution Stats to Your React App

How to Add GitHub Contribution Stats to Your React App

Barbara Streisand
Barbara StreisandOriginal
2025-01-15 09:10:47948browse

How to Add GitHub Contribution Stats to Your React App

Want to showcase your GitHub activity in your React portfolio? In this tutorial, I'll show you how to create a React component that displays your total GitHub contributions using GitHub's GraphQL API, complete with efficient caching. Let's build something cool! ?

What We'll Build

We'll create a React component that:

  • Fetches your GitHub contributions from 2020 to present
  • Includes both public and private contributions
  • Implements client-side caching to optimize performance
  • Shows a loading state while fetching
  • Handles errors gracefully

Prerequisites

Before we start, you'll need:

  1. A GitHub Personal Access Token (with read:user scope)
  2. A React project set up (using Create React App, Next.js, or your preferred setup)
  3. Basic knowledge of React hooks and async operations

Step 1: Setting Up the GitHub Token

First, create a .env file in your project root and add your GitHub token:

NEXT_PUBLIC_GITHUB_TOKEN=your_github_token_here

Step 2: Creating the Data Fetching Utility

Create a new file called githubApi.js:

export async function fetchGithubCommits(username) {
  const GITHUB_TOKEN = process.env.NEXT_PUBLIC_GITHUB_TOKEN;
  const CACHE_KEY = `github-commits-${username}`;
  const CACHE_TTL = 3600; // 1 hour in seconds

  if (!GITHUB_TOKEN) {
    console.error("No GitHub token found!");
    throw new Error("GitHub token is required");
  }

  const cachedData = getCachedData(CACHE_KEY);
  if (cachedData) {
    return cachedData.value;
  }

  try {
    const currentYear = new Date().getFullYear();
    const startYear = 2020;
    let totalCommits = 0;

    for (let year = startYear; year <= currentYear; year++) {
      const query = `
        query($username: String!, $from: DateTime!, $to: DateTime!) {
          user(login: $username) {
            contributionsCollection(from: $from, to: $to) {
              totalCommitContributions
              restrictedContributionsCount
            }
          }
        }
      `;

      const response = await fetch("https://api.github.com/graphql", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${GITHUB_TOKEN}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          query,
          variables: {
            username,
            from: `${year}-01-01T00:00:00Z`,
            to: `${year}-12-31T23:59:59Z`,
          },
        }),
      });

      const data = await response.json();
      if (data.errors) {
        throw new Error(data.errors[0].message);
      }

      const yearCommits =
        (data.data?.user?.contributionsCollection?.totalCommitContributions || 0) +
        (data.data?.user?.contributionsCollection?.restrictedContributionsCount || 0);

      totalCommits += yearCommits;
    }

    setCachedData(CACHE_KEY, totalCommits, CACHE_TTL);
    return totalCommits;
  } catch (error) {
    console.error("Error fetching GitHub commits:", error);
    throw error;
  }
}

function setCachedData(key, value, ttl) {
  const item = {
    value,
    timestamp: Date.now(),
    ttl: ttl * 1000,
  };
  localStorage.setItem(key, JSON.stringify(item));
}

function getCachedData(key) {
  try {
    const item = localStorage.getItem(key);
    if (!item) return null;

    const parsedItem = JSON.parse(item);
    const now = Date.now();

    if (now - parsedItem.timestamp > parsedItem.ttl) {
      localStorage.removeItem(key);
      return null;
    }

    return parsedItem;
  } catch {
    return null;
  }
}

export function invalidateCommitsCache(username) {
  localStorage.removeItem(`github-commits-${username}`);
}

Step 3: Creating the React Component

Create a new file called GitHubStats.js:

import React, { useState, useEffect } from 'react';
import { fetchGithubCommits } from './githubApi';

const GitHubStats = ({ username }) => {
  const [commits, setCommits] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchStats = async () => {
      try {
        setLoading(true);
        setError(null);
        const totalCommits = await fetchGithubCommits(username);
        setCommits(totalCommits);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchStats();
  }, [username]);

  if (loading) {
    return <div>Loading GitHub stats...</div>;
  }

  if (error) {
    return <div>Error: {error}</div>;
  }

  return (
    <div className="github-stats">
      <h2>GitHub Contributions</h2>
      <p>Total commits since 2020: {commits.toLocaleString()}</p>
    </div>
  );
};

export default GitHubStats;

Step 4: Adding Styles

Let's add some basic styling. Create GitHubStats.css:

.github-stats {
  padding: 20px;
  border-radius: 8px;
  background-color: #f6f8fa;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  margin: 20px 0;
}

.github-stats h2 {
  margin: 0 0 15px 0;
  color: #24292e;
}

.github-stats p {
  font-size: 1.2em;
  color: #586069;
}

Step 5: Using the Component

Now you can use the component in your app:

import GitHubStats from './GitHubStats';

function App() {
  return (
    <div className="App">
      <h1>My Developer Portfolio</h1>
      <GitHubStats username="your-github-username" />
    </div>
  );
}

Making it Better: Advanced Features

1. Adding a Refresh Button

Update GitHubStats.js to include a manual refresh option:

import React, { useState, useEffect } from 'react';
import { fetchGithubCommits, invalidateCommitsCache } from './githubApi';

const GitHubStats = ({ username }) => {
  // ... previous state declarations ...

  const handleRefresh = async () => {
    invalidateCommitsCache(username);
    await fetchStats();
  };

  return (
    <div className="github-stats">
      <h2>GitHub Contributions</h2>
      <p>Total commits since 2020: {commits.toLocaleString()}</p>
      <button onClick={handleRefresh} disabled={loading}>
        {loading ? 'Refreshing...' : 'Refresh Stats'}
      </button>
    </div>
  );
};

2. Adding Year-by-Year Breakdown

We can modify the component to show contributions per year:

NEXT_PUBLIC_GITHUB_TOKEN=your_github_token_here

Performance Tips

  1. Caching Strategy: The current implementation caches data for an hour. Adjust the CACHE_TTL based on your needs.
  2. Error Boundaries: Consider wrapping the component in an Error Boundary to handle unexpected errors gracefully.
  3. Loading States: Add a skeleton loader instead of a simple "Loading..." text for better UX.

Common Issues and Solutions

  1. CORS Issues: Make sure you're using the correct GitHub API endpoint and headers.
  2. Token Permissions: Ensure your GitHub token has the required permissions.
  3. Rate Limiting: Handle GitHub's API rate limits by checking the remaining rate limit in the response headers.

You now have a fully functional GitHub stats component in your React app! This implementation provides a solid foundation that you can build upon. Some ideas for enhancement:

  • Add more GitHub statistics (like stars, PRs, issues)
  • Create visual representations using charts
  • Add animations for loading and updates
  • Implement more detailed error handling

Remember to keep your GitHub token secure and never commit it to your repository. Happy coding! ?

The above is the detailed content of How to Add GitHub Contribution Stats to Your React App. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn