Skip to content

cyrus2281/github-widgets

Repository files navigation

GitHub Widgets

A flexible application for generating dynamic GitHub contribution widgets as SVG images. Supports serverless deployment (Netlify Functions), standalone server deployment (Express), and Docker containers. Designed to be embedded anywhere.

Widgets

GitHub Contribution Timeseries

time-series-sample

Experience Timeline

experience-timeline-sample

Most Starred Repositories

most-starred-sample

User Stats

user-stats-sample

Repository Card

repository-card-sample

Contents

Features

  • 🎨 Beautiful SVG Widgets - Animated, responsive, customizable widgets for GitHub ReadMe
  • 🌈 Customizable Themes - Choose from multiple color themes for widgets
  • Fast & Cached - In-memory LRU cache with configurable TTL (default: 1 hour)
  • 🔒 Secure - Optional user locking via LOCK_GITHUB_USER environment variable
  • 🚀 Flexible Deployment - Deploy as serverless functions (Netlify), standalone server (Express), or Docker container
  • 🔄 Extensible - Easy to add new widget types and API versions
  • 🛠 SVG Error Handling - All errors returned as SVG images with appropriate HTTP status codes

Themes

All widgets support customizable color themes to match your style preferences. Choose from 6 pre-built themes or use the default.

Available Themes

Use the theme query parameter to select a theme for any widget:

Theme Description Preview Colors
radical (default) Vibrant pink and purple with dark background Pink title, multi-color accents
ocean Cool blue tones inspired by the deep sea Cyan title, ocean blue palette
sunset Warm pink and orange hues Coral pink title, sunset gradients
forest Natural green tones Fresh green title, nature-inspired
midnight Deep purple and violet shades Purple title, night sky colors
monochrome Classic black, white, and gray Clean grayscale aesthetic

Usage

Add the theme parameter to any widget URL:

/api/v1/timeseries-history.svg?userName=octocat&theme=ocean

Previews

Radical Theme (Default):

Radical Theme

Ocean Theme:

Ocean Theme

Sunset Theme:

Sunset Theme

Forest Theme:

Forest Theme

Midnight Theme:

Midnight Theme

Monochrome Theme:

Monochrome Theme

Quick Start

Prerequisites

  • Node.js 20.x or higher (for local development)
  • GitHub Personal Access Token (create one here)
  • Netlify account (for serverless deployment) OR any server/hosting platform (for standalone deployment) OR Docker (for container deployment)

Installation

  1. Clone the repository:
git clone https://github.com/yourusername/github-widgets.git
cd github-widgets
  1. Install dependencies:
npm install
  1. Create a .env file from the example:
cp .env.example .env
  1. Add your GitHub token to .env:
GITHUB_TOKEN=ghp_your_token_here

Local Development

Option 1: Netlify Functions (Serverless)

Run the Netlify development server:

npm run dev

The API will be available at http://localhost:8888/api/v1/

Option 2: Standalone Server (Express)

Run the standalone Express server:

npm start

For development with auto-reload:

npm run dev:server

The API will be available at http://localhost:3000/api/v1/

Health check endpoint: http://localhost:3000/health

Testing

For Netlify Functions (port 8888):

# Timeseries history - with username and date range
curl "http://localhost:8888/api/v1/timeseries-history.svg?userName=cyrus2281&range=2025-01-01:2025-10-15"

# Timeseries history - with username only (defaults to last 365 days)
curl "http://localhost:8888/api/v1/timeseries-history.svg?userName=cyrus2281"

# Experience timeline - with CSV data
CSV_DATA="company,start,end,title,logo,color%0AGoogle,2025-10,,AI/ML%20Engineer,,#4285F4"
curl "http://localhost:8888/api/v1/experience-timeline.svg?experienceCSV=${CSV_DATA}"

# Most starred repositories
curl "http://localhost:8888/api/v1/most-starred.svg?userName=torvalds&top=5"

For Standalone Server (port 3000):

# Health check
curl "http://localhost:3000/health"

# Timeseries history
curl "http://localhost:3000/api/v1/timeseries-history.svg?userName=cyrus2281"

# Most starred repositories
curl "http://localhost:3000/api/v1/most-starred.svg?userName=torvalds&top=5"

API Documentation

Base URL

Netlify Functions (Serverless):

  • Production: https://your-site.netlify.app/api/v1/
  • Local Development: http://localhost:8888/api/v1/

Standalone Server (Express):

  • Production: https://your-domain.com/api/v1/ (or http://your-server-ip:3000/api/v1/)
  • Local Development: http://localhost:3000/api/v1/

Endpoints

GET /api/v1/timeseries-history.svg

time-series-sample

Generate a GitHub contribution timeseries chart as an SVG image.

Query Parameters:

Parameter Type Required Description
userName string Conditional* GitHub username to generate chart for
range string Optional Date range in format YYYY-MM-DD:YYYY-MM-DD (max 365 days)
theme string Optional Color theme: radical (default), ocean, sunset, forest, midnight, monochrome

*Required unless LOCK_GITHUB_USER environment variable is set.

Examples:

# Basic usage
/api/v1/timeseries-history.svg?userName=octocat

# With date range
/api/v1/timeseries-history.svg?userName=octocat&range=2024-01-01:2024-12-31

# With custom theme
/api/v1/timeseries-history.svg?userName=octocat&theme=ocean

Response:

  • Content-Type: image/svg+xml
  • Cache-Control: public, max-age=3600
  • X-Cache: HIT or MISS (indicates cache status)

GET /api/v1/experience-timeline.svg

experience-timeline-sample

Generate a professional experience timeline as an SVG image.

Query Parameters:

Parameter Type Required Description
experienceCSV string Yes URI-encoded CSV data with experience entries
includeStartDate boolean Optional Whether to display start date labels on timeline nodes. Defaults to true.
includeEndDate boolean Optional Whether to display end date labels on timeline nodes. Defaults to true.
width number Optional Width of the SVG in pixels. Defaults to 1200.
heightPerLane number Optional Height per timeline lane in pixels. Defaults to 80.
marginTop number Optional Top margin in pixels. Defaults to 100.
marginRight number Optional Right margin in pixels. Defaults to 30.
marginBottom number Optional Bottom margin in pixels. Defaults to 30.
marginLeft number Optional Left margin in pixels. Defaults to 30.
baseFontSize number Optional Base font size in pixels for relative scaling of all text. All font sizes scale proportionally from this value. Defaults to 14.
embedLogos boolean Optional Whether to embed company logos in the timeline. Defaults to true.
animationTotalDuration number Optional Total duration of the animation in seconds. Defaults to 5.
theme string Optional Color theme: radical (default), ocean, sunset, forest, midnight, monochrome

CSV Format:

The CSV must have the following header (in this exact order):

company,start,end,title,logo,color

Field Descriptions:

  • company (required): Company name
  • start (required): Start date in format YYYY, YYYY-MM, or YYYY-MM-DD
  • end (optional): End date in same format as start, or empty for "present"
  • title (optional): Job title/position
  • logo (optional): URL to company logo image
  • color (optional): Hex color code for the timeline bar (e.g., #4285F4)

Example CSV:

company,start,end,title,logo,color
Google,2025-10,,AI/ML Engineer,https://example.com/google-logo.png,#4285F4
Spotify,2024-08,2025-06,Sr Software Developer,https://example.com/spotify-logo.png,#1DB954
Netflix,2024-04,2024-12,Software Engineer - Contract,,#E50914
Amazon,2022-01,2024-08,Software Developer,https://example.com/amazon-logo.png,#FF9900
Meta,2020-06,2021-10,Web Developer,https://example.com/meta-logo.png,#0668E1

Usage Examples:

# Basic example with minimal data
CSV="company,start,end,title,logo,color%0AGoogle,2025-10,,AI/ML%20Engineer,,#4285F4"
curl "http://localhost:8888/api/v1/experience-timeline.svg?experienceCSV=${CSV}"

# With multiple entries (URL encode the entire CSV)
# Use online URL encoder or encodeURIComponent() in JavaScript

# Customizing dimensions and animation
CSV="company,start,end,title,logo,color%0AGoogle,2025-10,,AI/ML%20Engineer,,#4285F4"
curl "http://localhost:8888/api/v1/experience-timeline.svg?experienceCSV=${CSV}&width=800&heightPerLane=60&animationTotalDuration=10"
  • Use width, margin, and font size parameters to fit your layout.

JavaScript Example:

const experienceData = `company,start,end,title,logo,color
Google,2025-10,,AI/ML Engineer,https://example.com/logo.png,#4285F4
Spotify,2024-08,2025-06,Sr Software Developer,,#1DB954`;

const encodedCSV = encodeURIComponent(experienceData);
const url = `https://your-site.netlify.app/api/v1/experience-timeline.svg?experienceCSV=${encodedCSV}`;

Response:

  • Content-Type: image/svg+xml
  • Cache-Control: public, max-age=3600
  • X-Cache: HIT or MISS (indicates cache status)

Error Responses:

All errors are returned as SVG images with appropriate HTTP status codes:

  • 400 Bad Request - Invalid CSV format, missing required fields, or invalid dates
  • 404 Not Found - Invalid endpoint
  • 500 Internal Server Error - Server error during SVG generation

GET /api/v1/most-starred.svg

most-starred-sample

Generate a widget displaying your most starred GitHub repositories with animated glowing borders.

Query Parameters:

Parameter Type Required Description
userName string Conditional* GitHub username to fetch repositories for
top number Optional Number of repositories to display (1-10). Defaults to 3.
title string Optional Custom title for the widget. Defaults to "Most Starred".
animationDuration number Optional Duration of card entrance animations in seconds (0.5-10). Defaults to 3.5.
theme string Optional Color theme: radical (default), ocean, sunset, forest, midnight, monochrome

*Required unless LOCK_GITHUB_USER environment variable is set.

Examples:

# Basic usage - top 3 repositories (default)
/api/v1/most-starred.svg?userName=torvalds

# Custom number of repositories
/api/v1/most-starred.svg?userName=torvalds&top=5

# Custom title and theme
/api/v1/most-starred.svg?userName=torvalds&top=5&title=Top%20Projects&theme=ocean

# Custom animation duration (faster animations)
/api/v1/most-starred.svg?userName=torvalds&animationDuration=2

# All parameters
/api/v1/most-starred.svg?userName=torvalds&top=5&title=My%20Best%20Work&theme=midnight&animationDuration=4.5

Response:

  • Content-Type: image/svg+xml
  • Cache-Control: public, max-age=3600
  • X-Cache: HIT or MISS (indicates cache status)

Error Responses:

  • 400 Bad Request - Invalid username or top parameter out of range (1-10)
  • 404 Not Found - User not found or no repositories available
  • 500 Internal Server Error - Server error during SVG generation

GET /api/v1/user-stats.svg

user-stats-sample

Generate a comprehensive GitHub user statistics widget displaying various metrics with an animated GitHub logo.

Query Parameters:

Parameter Type Required Description
userName string Conditional* GitHub username to fetch statistics for
showHandle boolean Optional Display user's GitHub handle (@username). Defaults to true.
showStars boolean Optional Display total stars across all repositories. Defaults to true.
showCommits boolean Optional Display total commits (all time). Defaults to true.
showCommitsThisYear boolean Optional Display commits for the current year. Defaults to true.
showPRs boolean Optional Display total pull requests. Defaults to true.
showIssues boolean Optional Display total issues. Defaults to true.
showRepos boolean Optional Display total repositories. Defaults to true.
showContributedTo boolean Optional Display number of repositories contributed to. Defaults to true.
showLogo boolean Optional Show/hide the GitHub logo. When hidden, widget width adjusts to 300px. Defaults to true.
width number Optional Width of the SVG in pixels (300-1000). Defaults to 600.
animationDuration number Optional Duration of animations in seconds (0.5-10). Defaults to 2.
theme string Optional Color theme: radical (default), ocean, sunset, forest, midnight, monochrome

*Required unless LOCK_GITHUB_USER environment variable is set.

Examples:

# Basic usage - all stats visible (default)
/api/v1/user-stats.svg?userName=octocat

# Show only specific stats
/api/v1/user-stats.svg?userName=octocat&showCommits=false&showIssues=false

# Custom width
/api/v1/user-stats.svg?userName=octocat&width=600

# With custom theme
/api/v1/user-stats.svg?userName=octocat&theme=ocean

# Minimal stats display
/api/v1/user-stats.svg?userName=octocat&showHandle=false&showCommitsThisYear=false&showPRs=false&showIssues=false

# Custom animation speed
/api/v1/user-stats.svg?userName=octocat&animationDuration=4

# Hide the GitHub logo (widget becomes narrower at 300px)
/api/v1/user-stats.svg?userName=octocat&showLogo=false

# All parameters combined
/api/v1/user-stats.svg?userName=octocat&width=700&theme=midnight&animationDuration=3&showCommitsThisYear=false

Response:

  • Content-Type: image/svg+xml
  • Cache-Control: public, max-age=3600
  • X-Cache: HIT or MISS (indicates cache status)

Error Responses:

  • 400 Bad Request - Invalid username, width out of range (300-1000), or animationDuration out of range (0.5-10)
  • 404 Not Found - User not found
  • 500 Internal Server Error - Server error during SVG generation

GET /api/v1/repository-card.svg

repository-card-sample

Generate a repository card widget displaying GitHub repository information similar to GitHub's pinned repositories.

Query Parameters:

Parameter Type Required Description
userName string Conditional* GitHub username (repository owner)
repoName string Yes Repository name
theme string Optional Color theme: radical (default), ocean, sunset, forest, midnight, monochrome
showUserName boolean Optional Display the username/owner. Defaults to true.
showLanguage boolean Optional Display the primary language. Defaults to true.
showStars boolean Optional Display star count. Defaults to true.
showForks boolean Optional Display fork count. Defaults to true.
width number Optional Width of the card in pixels (300-600). Defaults to 400.
height number Optional Height of the card in pixels (100-200). Defaults to 120.

*Required unless LOCK_GITHUB_USER environment variable is set.

Examples:

# Basic usage
/api/v1/repository-card.svg?userName=octocat&repoName=github-widgets

# With custom theme
/api/v1/repository-card.svg?userName=octocat&repoName=github-widgets&theme=ocean

# Hide specific elements
/api/v1/repository-card.svg?userName=octocat&repoName=github-widgets&showUserName=false&showForks=false

# Custom dimensions
/api/v1/repository-card.svg?userName=octocat&repoName=github-widgets&width=500&height=150

# All parameters combined
/api/v1/repository-card.svg?userName=octocat&repoName=github-widgets&theme=midnight&width=550&height=140&showLanguage=false

Response:

  • Content-Type: image/svg+xml
  • Cache-Control: public, max-age=3600
  • X-Cache: HIT or MISS (indicates cache status)

Error Responses:

  • 400 Bad Request - Invalid username, repository name, or parameters out of range
  • 404 Not Found - Repository not found
  • 500 Internal Server Error - Server error during SVG generation

Embedding in Markdown

Netlify Deployment:

![GitHub Contributions](https://your-site.netlify.app/api/v1/timeseries-history.svg?userName=octocat)

Standalone Server Deployment:

![GitHub Contributions](https://your-domain.com/api/v1/timeseries-history.svg?userName=octocat)

Embedding in HTML

Netlify Deployment:

<img src="https://your-site.netlify.app/api/v1/timeseries-history.svg?userName=octocat" alt="GitHub Contributions" />

Standalone Server Deployment:

<img src="https://your-domain.com/api/v1/timeseries-history.svg?userName=octocat" alt="GitHub Contributions" />

Environment Variables

Required

  • GITHUB_TOKEN - GitHub Personal Access Token

Optional

  • LOCK_GITHUB_USER - Lock API to specific GitHub user

    • When set, userName query parameter is disabled
    • Useful for personal deployments
    • Example: LOCK_GITHUB_USER=cyrus2281
  • CACHE_MAX_SIZE - Maximum number of cached responses

    • Default: 100
    • Increase for high-traffic deployments
  • CACHE_TTL_MS - Cache time-to-live in milliseconds

    • Default: 3600000 (1 hour)
    • Adjust based on update frequency needs
  • PORT - Server port (Standalone Server only)

    • Default: 3000
    • Only used when running the standalone Express server
    • Example: PORT=8080

Deployment

This application supports two deployment options, each with distinct advantages:

Deployment Option 1: Netlify Functions (Serverless)

Setup:

  1. Via Netlify CLI:
# Login to Netlify
netlify login

# Deploy to production
npm run deploy
  1. Via Git Integration:
  • Push your code to GitHub
  • Connect repository in Netlify dashboard
  • Netlify will auto-deploy on push to main branch
  1. Set Environment Variables:

In Netlify dashboard or via CLI:

netlify env:set GITHUB_TOKEN "ghp_your_token_here"
netlify env:set LOCK_GITHUB_USER "your-username"  # Optional
netlify env:set CACHE_MAX_SIZE "100"  # Optional
netlify env:set CACHE_TTL_MS "3600000"  # Optional

Access your API:

https://your-site.netlify.app/api/v1/timeseries-history.svg?userName=octocat

Deployment Option 2: Standalone Server (Express)

Setup:

  1. Install dependencies:
npm install
  1. Configure environment variables:

Create a .env file:

cp .env.example .env

Edit .env with your configuration:

  1. Start the server:

Production mode:

npm start

Development mode (with auto-reload):

npm run dev:server

The server will start on port 3000 (or the PORT specified in .env).

  1. Verify the server is running:
# Health check
curl http://localhost:3000/health

# Test an endpoint
curl "http://localhost:3000/api/v1/user-stats.svg?userName=octocat"

Deployment Option 3: Docker Container

Prerequisites:

Quick Start:

Pull and run the pre-built image from Docker Hub:

docker pull cyrus2281/github-widgets:latest

docker run -d \
  --name github-widgets \
  -p 3000:3000 \
  -e GITHUB_TOKEN="ghp_your_token_here" \
  cyrus2281/github-widgets:latest

Access your API:

http://localhost:3000/api/v1/timeseries-history.svg?userName=octocat

Health Check:

curl http://localhost:3000/health

Environment Variables:

Configure the container using environment variables:

Variable Required Default Description
GITHUB_TOKEN Yes - GitHub Personal Access Token with read:user scope
PORT No 3000 Port the server listens on inside the container
LOCK_GITHUB_USER No - Lock API to specific GitHub user (disables userName parameter)
CACHE_MAX_SIZE No 100 Maximum number of cached responses
CACHE_TTL_MS No 3600000 Cache time-to-live in milliseconds (1 hour)

Advanced Usage:

1. Run with all configuration options:

docker run -d \
  --name github-widgets \
  -p 8080:3000 \
  -e GITHUB_TOKEN="ghp_your_token_here" \
  -e LOCK_GITHUB_USER="your-username" \
  -e CACHE_MAX_SIZE="200" \
  -e CACHE_TTL_MS="7200000" \
  --restart unless-stopped \
  cyrus2281/github-widgets:latest

2. Run with custom port mapping:

# Map container port 3000 to host port 8080
docker run -d \
  --name github-widgets \
  -p 8080:3000 \
  -e GITHUB_TOKEN="ghp_your_token_here" \
  cyrus2281/github-widgets:latest

# Access at http://localhost:8080/api/v1/...

3. View logs:

docker logs github-widgets

# Follow logs in real-time
docker logs -f github-widgets

4. Stop and remove container:

docker stop github-widgets
docker rm github-widgets

Building from Source (Optional):

If you want to build the Docker image yourself:

# Clone the repository
git clone https://github.com/cyrus2281/github-widgets.git
cd github-widgets

# Build the image
docker build -t github-widgets:local .

# Run your custom build
docker run -d \
  --name github-widgets \
  -p 3000:3000 \
  -e GITHUB_TOKEN="ghp_your_token_here" \
  github-widgets:local

Production Deployment Tips:

1. Use specific version tags:

docker pull cyrus2281/github-widgets:v1.0.0

2. Behind a reverse proxy (Nginx, Traefik, Caddy):

# Nginx example
location /github-widgets/ {
    proxy_pass http://localhost:3000/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_cache_valid 200 1h;
}

3. Kubernetes Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: github-widgets
spec:
  replicas: 3
  selector:
    matchLabels:
      app: github-widgets
  template:
    metadata:
      labels:
        app: github-widgets
    spec:
      containers:
      - name: github-widgets
        image: cyrus2281/github-widgets:latest
        ports:
        - containerPort: 3000
        env:
        - name: GITHUB_TOKEN
          valueFrom:
            secretKeyRef:
              name: github-widgets-secret
              key: github-token
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 10
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: github-widgets
spec:
  selector:
    app: github-widgets
  ports:
  - port: 80
    targetPort: 3000
  type: LoadBalancer

Architecture

The application follows a modular, extensible architecture:

  • API Router - Routes requests to versioned handlers
  • Handlers - Process requests and generate responses
  • Services - Business logic (GitHub API, SVG generation)
  • Utils - Shared utilities (cache, validation, errors)

Caching Strategy

  • In-Memory LRU Cache - Fast, simple, resets on cold starts
  • Cache Key Format: timeseries-history:{username}:{startDate}:{endDate}
  • TTL: 1 hour (configurable)
  • Max Size: 100 entries (configurable)
  • Eviction: Least Recently Used (LRU)

Cache headers:

  • X-Cache: HIT - Response served from cache
  • X-Cache: MISS - Response generated fresh

Adding New Endpoints

  1. Create handler in src/handlers/v1/new-endpoint.js:
export async function handler(event) {
  // Your logic here
  return {
    statusCode: 200,
    headers: { 'Content-Type': 'image/svg+xml' },
    body: svgContent,
  };
}
  1. Add route in netlify/functions/api.js:
case 'new-endpoint.svg':
  return newEndpointHandler(event);
  1. Add route in server/routes.js:
router.get(['/v1/new-endpoint.svg', '/v1/new-endpoint'], wrapHandler(newEndpointHandler));
  1. Reuse existing utilities (cache, validation, errors)

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Submit a pull request

License

Apache-2.0 License - see LICENSE file for details