My personal portfolio website, built with Next.js, Tailwind CSS and deployed to Vercel. Based on chronark's site. Some ideas borrowed from leerob/leerob.io.
It is supposed to be used as a template for other GitHub users' portfolios. Data about user and projects are gathered via GitHub and Vercel API.
- Framework: Next.js 16.3.3
- Deployment: Vercel
- Styling: Tailwind CSS 4.3.1
- UI: React 19.2.8
- Icons: React Icons 5.7.0
- Node.js: 24.x
The portfolio automatically displays comprehensive information for each repository in the /projects page. Here's what visitors can expect to see for each project:
- Project name with gradient styling and clickable links
- Description from GitHub repository
- Creation date showing when the project was started
- Star count with compact number formatting
- GitHub repository link for easy access to source code
- Visitor statistics: Unique repository visitors in the last 14 days and today
- Security alerts: Dependabot alerts categorized by severity (critical, high, medium, low)
- AI agent contributions: Counts of merged GitHub Copilot pull requests and Codex contributions, including labeled PRs and co-authored commits
- Vercel integration: Shows deployment status, Node.js version, and framework info
- Framework detection:
- Next.js projects with Pages Router, App Router, or hybrid detection
- Turbopack usage indicator
- Next.js version upgrade recommendations
- UI library detection: Automatically identifies Tailwind CSS, React Bootstrap, Primer, and other libraries from package.json
- GitHub API: Repository information, traffic data, security alerts, pull requests, and AI agent contribution signals
- Vercel API: Deployment information and project details
- GraphQL queries: Pinned repositories, organization data, Copilot-authored merged pull requests, Codex-labeled merged pull requests, and Codex co-authored commits
All data is cached and refreshed automatically to ensure good performance while providing up-to-date information.
git clone https://github.com/jirihofman/portfolio.git
cd portfolioCreate a .env.local file similar to .env.example.
cp .env.example .env.localAdd your tokens to the .env.local file:
# Optional but recommended: raises GitHub API rate limits and enables owner-only metrics
GH_TOKEN=YOUR_GH_TOKEN
# Optional: Vercel token to display deployment information
VC_TOKEN=YOUR_VERCEL_TOKEN
# Optional: Set to false when using your own data in data.json
IS_TEMPLATE=trueNote: The application can build and run without GH_TOKEN by using the username in data.json and unauthenticated GitHub API requests. Add a token for higher rate limits and owner-only data such as traffic and Dependabot metrics.
Then install dependencies and run the development server:
# Install dependencies.
npm install
# Replace jirihofman's personal info with octocat's.
npm run setup
# Start hacking.
npm run devEdit data.json to put your personal information there.
When using this template for your own portfolio:
- Run
npm run setupto replace the default personal information indata.jsonwith template data - Edit
data.jsonwith your own information (githubUsername, description, heroNames) - Set
IS_TEMPLATE=falsein your.env.localfile to prevent future builds from reverting your changes - Update the following files with your information:
-
README.md: Update the link at the top -
app/layout.jsx: Update metadata (title, description, favicon) - handled bysetup.mjs -
public/favicon.ico: Add your own favicon - handled bysetup.mjs -
vercel.json: Select the function region closest to your visitors and upstream APIs
-
After cloning or forking this repository, you may want to remove all template-specific functionality and references to the original author. This will prepare the repository to be your own personal portfolio.
Using GitHub Copilot, Codex, or another AI agent:
If you have access to GitHub Copilot Workspace, Codex, or another AI coding agent, you can use the removal prompt:
# Copy the prompt content and provide it to your AI agent
cat .github/prompts/remove-template.prompt.mdThe AI agent will:
- Remove template reversion logic from
lib/setup.mjs - Remove all references to jirihofman
- Eliminate IS_TEMPLATE environment variable usage
- Clean up README to focus on personal portfolio use
- Prepare the repository for your personal use without template features
Manual Removal:
If you prefer to remove template functionality manually, follow the detailed instructions in .github/prompts/remove-template.md.
Set OPENROUTER_MANAGEMENT_KEY in .env.local and restart the dev server to enable /ai-usage and its navigation links on the main portfolio. Without a key, the page returns 404 and the links are hidden. Other GitHub profiles never display the owner's AI usage links.
Use an OpenRouter management key, not a standard inference key. The activity API requires it and returns the last 30 completed UTC days. The key is used server-side for the activity endpoint and an aggregate analytics query for cached input tokens. Never use a NEXT_PUBLIC_ variable or commit a key.
The public page shows model names, daily request counts, input/output/reasoning token counts, model shares, and active days. It excludes prompts, conversations, account/member details, endpoint and key identifiers, and all spending data. Reasoning tokens are already part of output tokens. Cached input tokens are shown when the analytics API provides a complete result; unavailable cache data is omitted, not reported as zero. Cached tokens are already included in input tokens. Only allowlisted aggregates are cached for six hours, with revalidation on a subsequent request; an already open page does not poll. The timestamp shows when the displayed data was fetched, without a cache-duration label. Activity API failures show a neutral unavailable state; analytics failures omit the cached-token row while keeping the rest of the page available. No additional key or browser setup is needed.
Add OPENROUTER_MANAGEMENT_KEY in the portfolio project's Settings → Environment Variables before deploying this feature. Use a Sensitive variable for Production and Preview. Preview scope enables live statistics in PR deployments; omit that scope if previews should not access the account. Keep the value server-only and use the same management key as local development. Development can continue using the ignored .env.local file.
Environment changes apply to new deployments. Redeploy the relevant environment after adding, replacing, or removing the key so navigation and the page use the new configuration. Removing the variable disables the feature; it does not revoke the key at OpenRouter.
Run node --test lib/ai-usage.test.mjs for date boundaries, aggregation, privacy filtering, and cached-token parsing (including zero, missing, invalid, and partial results). Run npm run build-only to validate the Next.js production build without running the template setup script. With the dev server running, open /ai-usage and check the daily totals and token breakdown; without a key, expect a 404.
When the OpenRouter key is configured, the main profile can show a short sentence directly below the Copilot/Codex/Claude contribution summary, linking to AI usage. It uses the same six-hour cached aggregates for the last 30 completed UTC days, without another API request or any prompt data. Custom GitHub profiles never show this sentence.
The sentence requires at least 10 total requests, a leading model with at least 60% of requests, and a lead over the runner-up of at least 20 percentage points. These are conservative display heuristics, not a statistical confidence claim. With fewer than 100 requests to the leading model it says “I dabble with [model] on OpenRouter lately.” At 100 or more it says “I use [model] a lot on OpenRouter lately.” Missing, inconsistent, tied, or inconclusive data produces no sentence. Model versions are retained; unfamiliar model IDs are displayed as supplied rather than guessed.
Run node --test lib/model-preference.test.mjs to check these thresholds and naming behavior.