{"id":32667020,"url":"https://github.com/kossakovsky/notion-github-widget","last_synced_at":"2026-04-08T22:32:21.759Z","repository":{"id":321861578,"uuid":"1087445234","full_name":"kossakovsky/notion-github-widget","owner":"kossakovsky","description":"GitHub contribution widget embeddable in Notion. View any user's contribution graph with live data from GitHub GraphQL API. Built with Next.js 16, React 19, TypeScript.","archived":false,"fork":false,"pushed_at":"2025-10-31T23:47:06.000Z","size":97,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-01T01:17:59.376Z","etag":null,"topics":["contribution-graph","github-api","nextjs","notion","react","tailwindcss","typescript","widget"],"latest_commit_sha":null,"homepage":"https://notion-github-widget.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kossakovsky.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-31T23:33:20.000Z","updated_at":"2025-10-31T23:47:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kossakovsky/notion-github-widget","commit_stats":null,"previous_names":["kossakovsky/notion-github-widget"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/kossakovsky/notion-github-widget","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kossakovsky%2Fnotion-github-widget","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kossakovsky%2Fnotion-github-widget/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kossakovsky%2Fnotion-github-widget/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kossakovsky%2Fnotion-github-widget/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kossakovsky","download_url":"https://codeload.github.com/kossakovsky/notion-github-widget/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kossakovsky%2Fnotion-github-widget/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31577444,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["contribution-graph","github-api","nextjs","notion","react","tailwindcss","typescript","widget"],"created_at":"2025-11-01T02:00:45.438Z","updated_at":"2026-04-08T22:32:21.754Z","avatar_url":"https://github.com/kossakovsky.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Contributions Widget for Notion\n\nA Next.js application that displays GitHub contribution graphs, embeddable directly into Notion pages. View any GitHub user's contribution history for the last year with a clean, interactive visualization.\n\n## ✨ Features\n\n- 📊 **GitHub Contribution Graphs** - Displays the iconic 53×7 grid showing daily contributions\n- 🎨 **Dual Theme Support** - Light and dark themes matching GitHub's style\n- ⚡ **Optimized Performance** - 1-hour server-side and HTTP caching\n- 🔒 **Security First** - Username validation, XSS protection, and input sanitization\n- 📱 **Responsive Design** - Works on desktop and mobile devices\n- 🌐 **Notion Ready** - Seamless embedding in Notion pages\n- 🎯 **Real-time Data** - Fetches live data from GitHub GraphQL API\n- 💾 **Smart Caching** - Reduces API calls while keeping data fresh\n- 📈 **Analytics Integration** - Built-in Vercel Analytics support\n- ⏱️ **Loading States** - Smooth skeleton animations while data loads\n\n## 🚀 Quick Start\n\n### Basic Usage\n\nSimply append any GitHub username to the URL:\n\n```\nhttps://notion-github-widget.vercel.app/[username]\n```\n\n### Examples\n\n```\n# View Linus Torvalds' contributions\nhttps://notion-github-widget.vercel.app/torvalds\n\n# View with light theme\nhttps://notion-github-widget.vercel.app/octocat?theme=light\n\n# View with dark theme (default)\nhttps://notion-github-widget.vercel.app/gaearon?theme=dark\n```\n\n### Theme Options\n\nControl the appearance with the `theme` URL parameter:\n\n- **Dark theme** (default): `?theme=dark` or no parameter\n- **Light theme**: `?theme=light`\n\n## 📝 Embedding in Notion\n\n1. **Create an Embed Block**\n   - In your Notion page, type `/embed`\n   - Select \"Embed\" from the menu\n\n2. **Paste the URL**\n   - Enter: `https://notion-github-widget.vercel.app/[username]`\n   - Replace `[username]` with the GitHub username you want to display\n\n3. **Adjust Size**\n   - Drag the embed block corners to resize\n   - Recommended minimum width: 800px for best visibility\n\n4. **Choose Theme** (Optional)\n   - Add `?theme=light` or `?theme=dark` to match your Notion page theme\n\n### Notion Embed Example\n\n```\nhttps://notion-github-widget.vercel.app/torvalds?theme=light\n```\n\n## 🛠️ Development\n\n### Prerequisites\n\n- **Node.js**: 20.x or higher\n- **Package Manager**: npm, yarn, pnpm, or bun\n- **Git**: For version control\n\n### Installation\n\n```bash\n# Clone the repository\ngit clone https://github.com/kossakovsky/notion-github-widget.git\ncd notion-github-widget\n\n# Install dependencies\nnpm install\n\n# Create environment file\ncp .env.example .env.local\n# Edit .env.local and add your GitHub token\n```\n\n### Available Scripts\n\n```bash\n# Development server (http://localhost:3000)\nnpm run dev\n\n# Production build\nnpm run build\n\n# Start production server (requires build first)\nnpm start\n\n# Lint code\nnpm run lint\n```\n\n### Development Server\n\nThe development server runs at `http://localhost:3000` with:\n- Hot Module Replacement (HMR)\n- Fast Refresh for React components\n- TypeScript checking\n- ESLint integration\n\n## 🏗️ Tech Stack\n\n| Technology | Version | Purpose |\n|-----------|---------|---------|\n| Next.js | 16.0.1 | React framework with App Router |\n| React | 19.2.0 | UI library |\n| TypeScript | ^5 | Type safety |\n| Tailwind CSS | ^4 | Styling |\n| Vercel Analytics | ^1.5.0 | Privacy-friendly analytics |\n\n### Key Technologies\n\n- **Next.js 16 App Router** - Server Components, streaming, and optimized rendering\n- **TypeScript Strict Mode** - Full type safety across the codebase\n- **Tailwind CSS 4** - Utility-first CSS with inline theme configuration\n- **GitHub GraphQL API** - Official API for contribution data\n- **Edge Runtime** - API routes optimized for global edge deployment\n\n## 📦 Project Structure\n\n```\nnotion-github-widget/\n├── app/\n│   ├── [username]/              # Dynamic user routes\n│   │   ├── page.tsx            # Contribution graph page\n│   │   └── not-found.tsx       # Custom 404 page\n│   ├── api/\n│   │   └── contributions/\n│   │       └── [username]/\n│   │           └── route.ts    # GitHub API endpoint (Edge Runtime)\n│   ├── layout.tsx              # Root layout with Analytics\n│   ├── page.tsx                # Landing page\n│   └── globals.css             # Global styles\n├── components/\n│   ├── ContributionGraph.tsx   # Main contribution widget (Client Component)\n│   ├── ContributionSkeleton.tsx # Loading skeleton\n│   └── ErrorDisplay.tsx        # Error state UI\n├── lib/\n│   ├── github.ts               # GitHub GraphQL client\n│   ├── validation.ts           # Security \u0026 validation utils\n│   └── types.ts                # TypeScript interfaces\n├── public/                     # Static assets\n├── CLAUDE.md                   # Claude Code documentation\n├── README.md                   # This file\n├── package.json                # Dependencies\n├── tsconfig.json               # TypeScript config\n└── next.config.ts              # Next.js config\n```\n\n## 🎨 Features in Detail\n\n### Contribution Graph Visualization\n\n- **Grid Layout**: 53 weeks × 7 days matching GitHub's design\n- **Color Intensity**: 5 levels based on contribution count\n  - Level 0: No contributions (gray)\n  - Level 1: 1-3 contributions (light green)\n  - Level 2: 4-6 contributions (medium green)\n  - Level 3: 7-9 contributions (darker green)\n  - Level 4: 10+ contributions (darkest green)\n- **Interactive Tooltips**: Hover to see exact contribution count and date\n- **Responsive Legend**: Shows intensity scale (Less → More)\n\n### Caching Strategy\n\n**Multi-Layer Caching for Optimal Performance:**\n\n1. **Server-Side Caching** (`revalidate: 3600`)\n   - Next.js revalidates data every hour\n   - Shared across all users viewing same username\n   - Reduces GitHub API calls significantly\n\n2. **HTTP Cache Headers**\n   ```\n   Cache-Control: public, s-maxage=3600, stale-while-revalidate=7200\n   ```\n   - Browser caching: 1 hour\n   - CDN caching: 1 hour\n   - Stale-while-revalidate: 2 hours (serves stale content while updating)\n\n3. **Benefits**:\n   - Faster page loads\n   - Reduced GitHub API usage (60 req/hour limit)\n   - Better user experience\n   - Lower server costs\n\n### Security Features\n\n**Comprehensive Input Protection:**\n\n1. **Username Validation**\n   - Regex: `^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$`\n   - Validates GitHub username rules:\n     - 1-39 characters\n     - Alphanumeric + hyphens\n     - Cannot start/end with hyphen\n     - No consecutive hyphens\n\n2. **XSS Protection**\n   - Sanitizes all URL parameters\n   - Removes dangerous characters\n   - React's JSX auto-escaping\n   - No dangerouslySetInnerHTML usage\n\n3. **Input Sanitization**\n   - `sanitizeUsername()`: Strips non-alphanumeric chars (except hyphens)\n   - `processUsername()`: Validates + sanitizes in one step\n   - Type-safe with TypeScript\n\n### Error Handling\n\n**Robust Error Management:**\n\n- **404 Not Found**: Invalid or non-existent GitHub users\n- **400 Bad Request**: Invalid username format\n- **500 Server Error**: GitHub API failures\n- **Rate Limiting**: Graceful handling of GitHub API limits\n- **User-Friendly Messages**: Clear, actionable error descriptions\n- **Custom 404 Page**: Branded error page with GitHub link\n\n### SEO \u0026 Social Sharing\n\n**Dynamic Metadata Generation:**\n\n- **Per-User Meta Tags**: Title and description based on username\n- **Open Graph**: Full OG tags for Facebook, LinkedIn, etc.\n  ```html\n  \u003cmeta property=\"og:title\" content=\"username's GitHub Contributions\" /\u003e\n  \u003cmeta property=\"og:description\" content=\"View username's activity...\" /\u003e\n  \u003cmeta property=\"og:url\" content=\"https://...\" /\u003e\n  ```\n- **Twitter Cards**: Optimized for Twitter sharing\n  ```html\n  \u003cmeta name=\"twitter:card\" content=\"summary_large_image\" /\u003e\n  ```\n- **Canonical URLs**: SEO-friendly canonical links\n- **Sitemap Ready**: Compatible with Next.js sitemap generation\n\n### Analytics\n\n**Privacy-Friendly Tracking:**\n\n- **Vercel Analytics** integration\n- No cookies required\n- GDPR compliant\n- Tracks:\n  - Page views\n  - User interactions\n  - Performance metrics (Web Vitals)\n- Dashboard access via Vercel deployment\n\n## 🔧 Configuration\n\n### Environment Variables\n\n**Required:**\n\nCreate a `.env.local` file in the root directory:\n\n```bash\nGITHUB_TOKEN=your_github_personal_access_token\n```\n\n**How to get a GitHub token:**\n\n1. Go to [GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)](https://github.com/settings/tokens/new)\n2. Click \"Generate new token (classic)\"\n3. Give it a name (e.g., \"Notion GitHub Widget\")\n4. Select the following scope:\n   - ✅ `read:user` - Read user profile data\n5. Click \"Generate token\"\n6. Copy the token and add it to your `.env.local` file\n\n**Note:** Keep your token secure and never commit it to version control!\n\n**Optional for production:**\n- Vercel deployment automatically enables Analytics\n- Add `GITHUB_TOKEN` to your Vercel environment variables\n\n### GitHub API Rate Limits\n\n**With Authentication:**\n- 5,000 requests per hour\n- Sufficient for production use\n- Per-token limit\n\n**Caching Benefits:**\n- First request: Fetches from GitHub\n- Subsequent requests (\u003c 1 hour): Served from cache\n- Effectively: ~1 API call per username per hour\n\n## 🐛 Troubleshooting\n\n### Common Issues\n\n**Widget not loading in Notion**\n- Ensure URL is correct: `https://notion-github-widget.vercel.app/[username]`\n- Check Notion embed block size (minimum 800px width recommended)\n- Try refreshing the Notion page\n\n**User not found error**\n- Verify GitHub username is correct\n- Username is case-sensitive\n- User must exist on GitHub.com\n\n**Rate limit errors**\n- Wait for the rate limit to reset (1 hour)\n- Caching should prevent most rate limit issues\n- Consider deploying your own instance for higher limits\n\n**Slow loading**\n- First load fetches fresh data (slower)\n- Cached requests are near-instant\n- Check GitHub API status: https://www.githubstatus.com/\n\n## 🤝 Contributing\n\nContributions are welcome! Here's how you can help:\n\n1. **Fork the repository**\n2. **Create a feature branch**: `git checkout -b feature/amazing-feature`\n3. **Make your changes**\n4. **Run tests**: `npm run lint \u0026\u0026 npm run build`\n5. **Commit**: `git commit -m 'Add amazing feature'`\n6. **Push**: `git push origin feature/amazing-feature`\n7. **Open a Pull Request**\n\n### Development Guidelines\n\n- Follow existing code style\n- Add TypeScript types for all functions\n- Test with multiple usernames\n- Ensure ESLint passes\n- Update documentation if needed\n\n## 📄 License\n\nApache License 2.0 - see [LICENSE](LICENSE) for details\n\n## 🔗 Links\n\n- **Live Demo**: [notion-github-widget.vercel.app](https://notion-github-widget.vercel.app)\n- **GitHub Repository**: [github.com/kossakovsky/notion-github-widget](https://github.com/kossakovsky/notion-github-widget)\n- **Report Issues**: [GitHub Issues](https://github.com/kossakovsky/notion-github-widget/issues)\n- **GitHub API Docs**: [docs.github.com/graphql](https://docs.github.com/en/graphql)\n\n## 🙏 Acknowledgments\n\n- Built with [Next.js](https://nextjs.org/)\n- Powered by [GitHub GraphQL API](https://docs.github.com/en/graphql)\n- Styled with [Tailwind CSS](https://tailwindcss.com/)\n- Deployed on [Vercel](https://vercel.com/)\n\n---\n\nMade with ❤️ for the GitHub community\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkossakovsky%2Fnotion-github-widget","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkossakovsky%2Fnotion-github-widget","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkossakovsky%2Fnotion-github-widget/lists"}