Documentation Index
Fetch the complete documentation index at: https://mintlify.com/David-Andino/github-dashboard/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The useGitHub hook encapsulates all GitHub API logic, including authentication, request handling, rate limit tracking, and error management.
Location: src/hooks/useGitHub.js
Import
import { useGitHub } from './hooks/useGitHub'
Hook API
Return Values
The hook returns an object with the following properties:
const {
token, // Current authentication token
saveToken, // Function to save/remove token
rateLimit, // Rate limit status object
getUser, // Fetch user profile
getRepos, // Fetch user repositories
getCommits, // Fetch repository commits
getLanguages // Fetch repository languages
} = useGitHub()
State Management
token
Current GitHub Personal Access Token. Initialized from localStorage on mount.
const [token, setToken] = useState(() => localStorage.getItem('gh_token') || '')
rateLimit
Rate limit information extracted from API response headers. null until first request.
Structure:
{
remaining: number, // Requests remaining
limit: number, // Total requests per hour
reset: number // Reset timestamp (milliseconds)
}
Functions
saveToken
Saves or removes the authentication token.
The GitHub Personal Access Token to save. Pass empty string to remove.
Signature:
saveToken(token: string) => void
Example:
// Save token
saveToken('ghp_abc123...')
// Remove token
saveToken('')
Implementation:
const saveToken = (t) => {
setToken(t)
if (t) localStorage.setItem('gh_token', t)
else localStorage.removeItem('gh_token')
}
getUser
Fetches a GitHub user’s profile.
The GitHub username to fetch
Signature:
getUser(username: string) => Promise<UserData>
Example:
try {
const userData = await getUser('torvalds')
console.log(userData.name) // "Linus Torvalds"
console.log(userData.public_repos) // Number of repos
} catch (error) {
console.error(error.message)
}
API Endpoint: GET /users/:username
Returns:
{
login: string
avatar_url: string
name: string
company: string | null
blog: string
location: string | null
bio: string | null
twitter_username: string | null
public_repos: number
public_gists: number
followers: number
following: number
// ... additional fields
}
getRepos
Fetches a user’s repositories with pagination support.
Page number for pagination
Number of repositories per page
Signature:
getRepos(username: string, page?: number, per_page?: number) => Promise<RepoData[]>
Example:
// Fetch first page (30 repos)
const repos = await getRepos('username')
// Fetch second page
const moreRepos = await getRepos('username', 2, 30)
// Custom page size
const topRepos = await getRepos('username', 1, 10)
API Endpoint: GET /users/:username/repos
Query Parameters:
page - Page number
per_page - Results per page
sort=updated - Sort by last updated
direction=desc - Descending order
Implementation:
const getRepos = (username, page = 1, per_page = 30) =>
request(`/users/${username}/repos`, {
page,
per_page,
sort: 'updated',
direction: 'desc'
})
Returns:
[
{
id: number
name: string
full_name: string
description: string | null
html_url: string
stargazers_count: number
forks_count: number
language: string | null
updated_at: string
size: number
license: { name: string } | null
open_issues_count: number
// ... additional fields
}
]
getCommits
Fetches recent commits from a repository.
Repository owner username
Number of commits to fetch
Signature:
getCommits(owner: string, repo: string, per_page?: number) => Promise<CommitData[]>
Example:
// Fetch last 10 commits
const commits = await getCommits('torvalds', 'linux')
// Fetch last 50 commits
const moreCommits = await getCommits('torvalds', 'linux', 50)
commits.forEach(commit => {
console.log(commit.commit.message)
console.log(commit.sha)
console.log(commit.author.login)
})
API Endpoint: GET /repos/:owner/:repo/commits
Implementation:
const getCommits = (owner, repo, per_page = 10) =>
request(`/repos/${owner}/${repo}/commits`, { per_page })
Returns:
[
{
sha: string
commit: {
author: {
name: string
email: string
date: string
}
message: string
}
author: {
login: string
avatar_url: string
} | null
}
]
getLanguages
Fetches programming languages used in a repository.
Repository owner username
Signature:
getLanguages(owner: string, repo: string) => Promise<Record<string, number>>
Example:
const languages = await getLanguages('facebook', 'react')
// { "JavaScript": 12345, "TypeScript": 5678, "CSS": 1234 }
const total = Object.values(languages).reduce((a, b) => a + b, 0)
Object.entries(languages).forEach(([lang, bytes]) => {
const percentage = ((bytes / total) * 100).toFixed(1)
console.log(`${lang}: ${percentage}%`)
})
API Endpoint: GET /repos/:owner/:repo/languages
Implementation:
const getLanguages = (owner, repo) =>
request(`/repos/${owner}/${repo}/languages`)
Returns:
Record<string, number> // { languageName: bytesOfCode }
Values represent bytes of code, not file counts. Use for percentage calculations.
Usage in App.jsx
Basic Setup
import { useGitHub } from './hooks/useGitHub'
function App() {
const {
token,
saveToken,
rateLimit,
getUser,
getRepos,
getCommits,
getLanguages
} = useGitHub()
// ... component logic
}
User Search Pattern
const search = async (username) => {
setLoading(true)
setError(null)
try {
const [userData, reposData] = await Promise.all([
getUser(username),
getRepos(username, 1, 30),
])
setUser(userData)
setRepos(reposData)
setHasMore(reposData.length === 30)
} catch (e) {
setError(e.message)
} finally {
setLoading(false)
}
}
The pattern uses Promise.all to fetch user data and repositories in parallel, reducing total request time.
useEffect(() => {
if (!currentUsername || page === 1) return
setLoading(true)
getRepos(currentUsername, page, 30)
.then(data => {
setRepos(data)
setHasMore(data.length === 30)
setSelectedRepo(null)
window.scrollTo({ top: 0, behavior: 'smooth' })
})
.catch(e => setError(e.message))
.finally(() => setLoading(false))
}, [page])
Commits on Demand
// In CommitPanel component
useEffect(() => {
setLoading(true)
getCommits(username, repo.name)
.then(setCommits)
.catch(err => setError(err.message))
.finally(() => setLoading(false))
}, [repo.name, username, getCommits])
Language Aggregation
// In LanguageChart component
const repos = repos.slice(0, 12) // Limit to first 12 repos
const promises = repos.map(r =>
getLanguages(username, r.name)
.then(langs => ({ ...langs }))
.catch(() => ({}))
)
const results = await Promise.all(promises)
// Aggregate bytes by language
const aggregated = {}
results.forEach(langs => {
Object.entries(langs).forEach(([lang, bytes]) => {
aggregated[lang] = (aggregated[lang] || 0) + bytes
})
})
Error Handling
All functions throw errors that should be caught with try-catch:
try {
const user = await getUser('nonexistent')
} catch (error) {
console.error(error.message) // "Usuario no encontrado"
}
See Error Handling for complete patterns.
Internal Implementation
Request Function
All API functions use the internal request helper:
const request = useCallback(async (path, params = {}) => {
const url = new URL(`${BASE}${path}`)
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v))
const res = await fetch(url.toString(), { headers: headers() })
// Extract rate limit
const remaining = res.headers.get('x-ratelimit-remaining')
const limit = res.headers.get('x-ratelimit-limit')
const reset = res.headers.get('x-ratelimit-reset')
if (remaining !== null) {
setRateLimit({ remaining: +remaining, limit: +limit, reset: +reset * 1000 })
}
// Error handling
if (res.status === 403) {
const data = await res.json()
throw new Error(data.message || 'Rate limit exceeded')
}
if (res.status === 404) throw new Error('Usuario no encontrado')
if (!res.ok) {
const data = await res.json()
throw new Error(data.message || `HTTP ${res.status}`)
}
return res.json()
}, [headers])
const headers = useCallback(() => {
const h = { 'Accept': 'application/vnd.github+json' }
if (token) h['Authorization'] = `Bearer ${token}`
return h
}, [token])
All functions are wrapped in useCallback to prevent unnecessary re-renders. Include them in dependency arrays when using in useEffect.
Memoization
const getUser = useCallback(
(username) => request(`/users/${username}`),
[request]
)
The request function itself depends on headers, which depends on token. This creates a stable reference chain:
token → headers → request → getUser/getRepos/getCommits/getLanguages
Only when token changes do all functions get new references.
Type Safety
While the hook is written in JavaScript, here’s the TypeScript equivalent for reference:
interface RateLimit {
remaining: number
limit: number
reset: number
}
interface UseGitHubReturn {
token: string
saveToken: (token: string) => void
rateLimit: RateLimit | null
getUser: (username: string) => Promise<any>
getRepos: (username: string, page?: number, per_page?: number) => Promise<any[]>
getCommits: (owner: string, repo: string, per_page?: number) => Promise<any[]>
getLanguages: (owner: string, repo: string) => Promise<Record<string, number>>
}
function useGitHub(): UseGitHubReturn