Re-checked 2026-10-01 against GitHub REST/GraphQL docs
Core approach is unchanged (public data without auth, OAuth/GitHub App for actions). Corrected: there is no REST endpoint for discussions (use GraphQL), the issues list also returns pull requests,
Authorization: Beareris the documented header, reaction preview headers are no longer needed, and Node 18 is end of life.
What changed in 2026
-
API versioning: current docs show
X-GitHub-Api-Version: 2026-03-10withAccept: application/vnd.github+json; pin a version in your portal. -
Issues API: now supports issue types (
type), sub-issues (parent_issue_id,sub_issues_summary) and issue fields; a portal can render hierarchy and types. GitHub added “Relates to” issue relationships and private saved views (September 2026 changelog). -
Rate limits (docs): 60/hour unauthenticated, 5,000/hour authenticated, 15,000/hour for Enterprise Cloud organizations; GitHub App installations get at least 5,000 and scale up to 12,500 (non-Enterprise); secondary limits (100 concurrent requests, 900 points per minute REST, 80 content-creating requests per minute).
-
Tooling (nodejs.org, endoflife.date and nextjs.org checked 2026-10-02): Node 26 is Current (released 2026-05-05), Node 24 “Krypton” is LTS (active support ends 2026-10-20, security to 2028-04-30), Node 22 is in maintenance, Node 18 and 20 are end of life; Next.js 16 is the current major (16.3 released 2026-08-03; 16.3.8 security patch 2026-09-30; 15.5 is maintenance); NextAuth.js is now Auth.js, which became part of Better Auth (docs, 2026); the
octokitpackage (REST, GraphQL, apps, webhooks) is actively maintained. GitHub CLI hasgh discussioncommands (2026-06-10) if you only need scripted access. -
AI: GitHub’s Copilot SDK (v0.1.x as of its March 2026 blog, requires Copilot CLI and a Copilot subscription or BYOK) is GitHub’s own example for AI issue summaries in a custom front end; see github-ui-alternatives-market-gap.
Overview
A custom GitHub API portal is a DIY approach to building a simplified, customer-facing frontend for GitHub issues and discussions. It uses the GitHub API to fetch data and a frontend framework (React, Vue, Next.js) to display issues in a simplified interface without GitHub’s complexity.
Why Build a Custom Portal?
Advantages
- Total control over UI/UX
- Simplified interface for non-technical users
- Brand customization (your colors, logo)
- Customer-facing without GitHub account requirement
- Cost-free (GitHub API is free for public data)
- No vendor lock-in
- Faster loading/responsiveness possible
- Dark mode, mobile optimization, custom search
Disadvantages
- Development effort (4-20 hours depending on features)
- Maintenance overhead (API changes, bugs)
- Hosting/infrastructure costs
- Authentication complexity
- Rate limiting considerations
Architecture Overview
Customer Browser
↓
Your Portal (React/Vue)
↓
GitHub API
↓
GitHub Issues/Discussions
Components:
- Frontend (React/Vue/Next.js) - Display layer
- Backend (optional - Node/Python) - Proxy for auth
- GitHub API - Data source
- Hosting (Vercel/Netlify) - Where portal lives
GitHub API Basics
Key Endpoints for Issues & Discussions
GET /repos/{owner}/{repo}/issues (returns pull requests too; filter on the pull_request key)
# Discussions: no REST endpoint; use the GraphQL API (repository(...) { discussions(first: 10) { nodes { ... } } })
GET /repos/{owner}/{repo}/issues/{issue_number}
POST /repos/{owner}/{repo}/issues (create issue)
PATCH /repos/{owner}/{repo}/issues/{issue_number} (update)
GET /repos/{owner}/{repo}/issues/{issue_number}/comments
POST /repos/{owner}/{repo}/issues/{issue_number}/comments (comment)
Authentication
- Public data: No auth needed
- User actions (create/comment): OAuth required
- Header:
Authorization: Bearer <token>plusX-GitHub-Api-Version - Rate limits: 60 req/hour unauthenticated, 5,000 authenticated (15,000 on Enterprise Cloud orgs)
Tech Stack Options
Beginner-Friendly
HTML + JavaScript + Fetch API
- Simplest approach
- ~2-3 hours
- Works for read-only portals
- Limited interactivity
- Best for: Static display of issues
fetch('https://api.github.com/repos/owner/repo/issues')
.then(r => r.json())
.then(issues => {
issues.forEach(issue => {
console.log(issue.title)
})
}) Intermediate
React + GitHub API
- Modern, component-based
- ~4-8 hours
- Good interactivity
- Mobile-friendly
- Best for: Feature-complete portals
import { useState, useEffect } from 'react'
export default function IssueList() {
const [issues, setIssues] = useState([])
useEffect(() => {
fetch('https://api.github.com/repos/owner/repo/issues')
.then(r => r.json())
.then(setIssues)
}, [])
return (
<div>
{issues.map(issue => (
<div key={issue.id}>
<h3>{issue.title}</h3>
<p>{issue.body}</p>
</div>
))}
</div>
)
} Advanced
Next.js + Backend Auth
- Full-stack solution
- ~8-20 hours
- Support for user interactions
- Server-side rendering
- Best for: Production portals with authentication
Feature Scope by Effort
Minimal (2-3 hours) - Read-Only Display
- List all issues
- Filter by label/status
- Search by title
- Link to GitHub
Basic (4-6 hours) - Interactive
- Issue details page
- Comments display
- Sort/filter options
- Responsive design
- Mobile friendly
Full-Featured (8-20 hours) - Complete Portal
- User authentication (OAuth)
- Create new issues
- Comment on issues
- Real-time updates
- Vote/reaction system
- Search with advanced filters
- Responsive + dark mode
Implementation Steps
Step 1: Choose Frontend Framework
Recommendation: Start with Next.js + TypeScript
- Easy setup
- Built-in hosting option (Vercel)
- Good for this use case
- Scales well
npx create-next-app@latest github-portal --typescript
cd github-portal Step 2: Create Issue Display Component
// components/IssueList.jsx
import { useEffect, useState } from 'react'
export default function IssueList({ owner, repo }) {
const [issues, setIssues] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
const fetchIssues = async () => {
const url = `https://api.github.com/repos/${owner}/${repo}/issues`
const response = await fetch(url)
const data = await response.json()
setIssues(data)
setLoading(false)
}
fetchIssues()
}, [owner, repo])
if (loading) return <p>Loading...</p>
return (
<div className="issues-list">
{issues.map(issue => (
<div key={issue.id} className="issue-card">
<h3>{issue.title}</h3>
<p>{issue.body}</p>
<span className="status">{issue.state}</span>
<a href={issue.html_url} target="_blank">
View on GitHub
</a>
</div>
))}
</div>
)
} Step 3: Add Filtering & Search
// components/IssueFilter.jsx
import { useState } from 'react'
export default function IssueFilter({ onFilter }) {
const [status, setStatus] = useState('open')
const [search, setSearch] = useState('')
const handleFilter = () => {
onFilter({ status, search })
}
return (
<div className="filters">
<input
type="text"
placeholder="Search issues..."
value={search}
onChange={(e) => {
setSearch(e.target.value)
handleFilter()
}}
/>
<select value={status} onChange={(e) => {
setStatus(e.target.value)
handleFilter()
}}>
<option value="open">Open</option>
<option value="closed">Closed</option>
<option value="all">All</option>
</select>
</div>
)
} Step 4: Style & Customize
.issues-list {
display: grid;
gap: 1rem;
}
.issue-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem;
background: #fff;
}
.issue-card h3 {
margin: 0 0 0.5rem;
color: #333;
}
.issue-card .status {
display: inline-block;
padding: 0.25rem 0.5rem;
background: #f0f0f0;
border-radius: 4px;
font-size: 0.85rem;
}
.issue-card a {
color: #0070f3;
text-decoration: none;
} Step 5: Deploy
Option 1: Vercel (Easiest for Next.js)
npm i -g vercel
vercel Option 2: Netlify
npm run build
# Connect repository to Netlify Option 3: Self-Hosted (Docker)
FROM node:24-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
CMD ["npm", "start"] Advanced Features
Add Authentication (OAuth)
// Use Auth.js (formerly NextAuth.js)
import { signIn } from 'next-auth/react'
// Allow users to create issues
const createIssue = async (title, body) => {
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/issues`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ title, body })
}
)
return response.json()
} Real-Time Updates
- Use GitHub webhooks for notifications
- Poll API every 30 seconds
- WebSocket connection to custom backend
Voting/Reactions
// Add reaction to issue
const addReaction = async (issueNumber, reaction) => {
fetch(`https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/reactions`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: 'application/vnd.github+json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ content: reaction })
})
} Common Pitfalls & Solutions
Rate Limiting
- Problem: GitHub limits API calls
- Solution: Implement caching (Redis), use GitHub GraphQL (more efficient)
- Prevention: Display cache timestamps to users
Slow Loading
- Problem: API calls take time
- Solution: Server-side rendering (Next.js), caching, pagination
- Prevention: Load only recent 20 issues, lazy-load details
OAuth Complexity
- Problem: User authentication is hard
- Solution: Use Auth.js (formerly NextAuth.js) or Octokit
- Prevention: Start without auth, add later if needed
Stale Data
- Problem: Portal shows old information
- Solution: Refresh every 5-10 minutes, show last-updated timestamp
- Prevention: Cache with TTL, webhook updates
Comparison with No-Code Alternatives
| Feature | Custom Portal | Fider | GitHub Discussions |
|---|---|---|---|
| Development | 4-20 hours | 0 hours | 5 min setup |
| Cost | Hosting only: Netlify Free / Personal / Pro, Vercel Hobby free (non-commercial only) / Pro (vendor pricing pages, 2026-10-02); self-hosting varies | Free tier or Pro cloud; self-host | Free |
| Customization | 100% | Limited | Limited |
| Voting | DIY (reactions API) | Built-in | Upvotes and polls |
| GitHub Integration | Direct API (REST/GraphQL) | Limited | Native |
| Multi-repo | Easy | Not typical | Per-repo |
| Auth | DIY | Built-in | GitHub login |
Best For
Build custom portal if:
- You need specific UI/UX
- Brand customization critical
- You want learning opportunity
- You have developer resources
- Single repository focus
- You need complete control
Use alternatives if:
- Quick launch needed (GitHub Discussions)
- Feature voting important (Fider)
- No development capacity
- Multi-repo support needed
Maintenance Considerations
Ongoing Tasks
- Monitor API changes (REST API is versioned by date; pin
X-GitHub-Api-Version; the “30 days notice” claim in earlier text was not verified) - Update dependencies monthly
- Monitor error rates and logs
- Respond to user feedback
- Performance optimization
Annual Effort: ~10-20 hours
Tools & Libraries
Popular GitHub API Libraries
octokit/@octokit/rest- JavaScript/Node (maintained at github.com/octokit/octokit.js)PyGithub- Pythongo-github- Go
React UI Libraries
- Material-UI
- Chakra UI
- Tailwind CSS
- Bootstrap React
Hosting Options
- Vercel (best for Next.js)
- Netlify (good for React SPA)
- GitHub Pages (static sites)
- AWS/GCP/Azure (full control)
Example Project Structure
github-portal/
├── pages/
│ ├── index.jsx # Home
│ ├── issues/
│ │ └── [id].jsx # Issue detail
│ └── api/
│ └── issues.js # API proxy
├── components/
│ ├── IssueList.jsx
│ ├── IssueDetail.jsx
│ └── IssueFilter.jsx
├── styles/
│ └── globals.css
├── public/
│ └── logo.png
└── package.json
Getting Started Checklist
- Choose framework (Next.js recommended)
- Create local project
- Fetch issues from GitHub API
- Display in grid/list
- Add filtering (open/closed/all)
- Add search
- Style with CSS/Tailwind
- Add issue detail page
- Add comments display
- Deploy to Vercel/Netlify
- Add authentication (optional)
- Monitor performance
- Gather user feedback
- Iterate on UX
Success Metrics
- Page load time < 2 seconds
- Mobile responsiveness
- Search relevance
- User session length
- Return visitor rate
- Support ticket reduction
Open items
- Development-hour estimates (4-20 hours, annual effort) are editorial rules of thumb, not measured.
- Not re-checked: OAuth app vs GitHub App recommendation details, Tailwind/Material-UI/Chakra versions, GitHub Pages specifics. Example code was not executed.
- Client-side
fetchof the REST API from a browser exposes rate limits per visitor IP (60/hour unauthenticated); a server-side proxy with a token is advised but not benchmarked here.
Sources
- https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api (accessed 2026-10-01)
- https://docs.github.com/en/rest/issues/issues and https://docs.github.com/en/rest/reactions/reactions (accessed 2026-10-01)
- https://docs.github.com/en/graphql/guides/using-the-graphql-api-for-discussions (accessed 2026-10-01)
- https://github.com/octokit/octokit.js (accessed 2026-10-01)
- https://github.blog/changelog/month/09-2026/ (accessed 2026-10-01)
- https://github.blog/ai-and-ml/github-copilot/building-ai-powered-github-issue-triage-with-the-copilot-sdk/ (accessed 2026-10-01)
- Node/Next.js lifecycle: https://nodesource.com/blog/nodejs-18-20-eol-136-million-downloads-july-2026 and https://versionlog.com/nextjs/16/ (secondary; via search, accessed 2026-10-01)
- https://nodejs.org/en/about/previous-releases, https://endoflife.date/nodejs, https://nextjs.org/blog, https://authjs.dev/ (accessed 2026-10-02)
- https://vercel.com/pricing and https://www.netlify.com/pricing/ (accessed 2026-10-02)