A personal new-tab page you can make your own in about 5 minutes: a GitHub activity feed rendered like a changelog, and a row of shortcuts to the places you open every day. Static HTML/CSS/JS — no framework, no build step, no dependencies to install.
Point any browser's new-tab page at your deployed URL and you're done.
Personal name, GitHub handle, and recent-activity items in the screenshot above are blurred/fictionalized — the heatmap shape and rest of the layout are real.
Using a coding agent (Claude Code, Codex, Cursor, etc.)? Hand it this repo and tell it to read
AGENTS.md— it'll interview you for your name, GitHub username, and shortcuts, then set up and optionally deploy your personal copy for you. That's the fastest path through everything below.
- Search bar — autofocused on load, submits to Google or navigates
directly for URL-like input. Restores "open a tab, start typing" now that
the browser's own new-tab landing is overridden.
Option+K(Mac) /Ctrl+K(elsewhere) refocuses it from anywhere on the page. - Shipped — your real GitHub contribution graph (same per-day data as
your profile page, not an approximation — always a full year, regardless
of
maxDays) with the total contribution count for the year alongside it, plus a list of recent activity with relative timestamps and GitHub-style state icons. Out of the box (no token) this reads the public Events API — pushes, PRs opened, merged, new repos. With the optional token below, it switches to the Search API for a richer, more accurate breakdown (real commits, open/draft/merged/closed PRs). - AI Agents — a small icon-only dock (Claude, ChatGPT, Gemini) shown above Shortcuts, kept visually separate since it's a fixed set of assistant tabs rather than a growing bookmark list.
- Shortcuts — a configurable grid of links with official brand icons
(GitHub, Gmail, Google Calendar, Notion, LinkedIn, X, YouTube, or any URL
via a generic globe icon), muted by default and tinted with the real brand
color on hover. The first 9 each get a real keyboard hotkey —
Option+1..9on Mac,Ctrl+1..9elsewhere — shown right-aligned on the card. - Greeting — time-of-day message plus a small SVG moon-phase indicator, because why not.
- Light/dark — follows the OS/browser color scheme automatically, no toggle needed.
- Responsive — one column on mobile and tablet, a two-column layout (activity + shortcuts) from 1024px up.
- No server required — works as a fully static site. An optional serverless function upgrades GitHub data quality if you set it up (see below); everything else works without it.
git clone https://github.com/alunadev/anybrowser-newtab.git
cd anybrowser-newtab
cp config.example.js config.jsOpen config.js and fill in your name, GitHub username, and shortcuts. Then
preview it locally:
python3 -m http.server 4173
# open http://localhost:4173(Any static file server works — or just open index.html directly in a
browser.)
Everything you'll ever want to change lives in config.js (copied from
config.example.js, and gitignored so your personal data never ends up in
git history — even in your own fork):
| Field | Description |
|---|---|
name |
Used in the greeting: "Good morning, <name>". |
githubUser |
Your GitHub username. Powers the entire "Shipped" section. |
maxDays |
How many days back the recent-activity list looks (default 7). Doesn't affect the heatmap, which always shows a full year. |
agents |
Array of { label, url, icon } for the AI Agents dock. Icon must be claude, openai, gemini, or grok. |
shortcuts |
Array of { label, url, icon }. Order in the array = display order. |
Available icon values for shortcuts: github, notion, gmail, gcal,
linkedin, x, youtube, or globe (a generic fallback for any other
link). Icons are official brand SVG marks — see ICONS in app.js if you
want to add another one.
Deploys straight from your local folder, so your personal config.js goes
along even though it's gitignored (see .vercelignore, which is what makes
that safe — it tells Vercel CLI not to fall back to .gitignore when
deciding what to upload):
npm i -g vercel # once
vercel login # once
vercel --prodIf you'd rather have Vercel auto-deploy on every push, fork this repo
privately, commit your own config.js to your fork (it's your data, your
repo, your call), and connect that fork to a new Vercel project. Don't do
this on a public fork — your config.js would be visible to anyone.
- Chrome / Edge / Brave: install a "New Tab Redirect" extension and set your deployed URL.
- Firefox: install "New Tab Override" and set the URL.
- Safari: Settings → General → set Homepage to the URL, and set "New tabs open with: Homepage".
- Arc: doesn't support custom new-tab URLs; pin the tab or use the home button instead.
By default, GitHub activity comes from the public, unauthenticated Events API — no setup needed, and it's plenty for personal use.
If you want private-repo activity to show up too (or just higher rate
limits), api/github-stats.js is a Vercel serverless function that proxies
GitHub's Search API with a token that never reaches the browser. Set these
on your Vercel project (Settings → Environment Variables):
GITHUB_TOKEN— a fine-grained personal access token, read-only. Grant:- Repository permissions → Contents: Read-only (lets the token read commits).
- Repository permissions → Pull requests: Read-only (lets the token read PR data).
- Repository access: the specific private repos you want counted, or "All repositories".
GITHUB_USERNAME— required, must match the account the token belongs to.
Why this works with fine-grained tokens (and the old approach didn't):
an earlier version of this app used GitHub's Events feed
(GET /users/{username}/events), which has no fine-grained permission that
can unlock private activity at all — its own docs mention an "Events" user
permission that doesn't actually exist in the fine-grained system (it's only
an organization-level permission for an unrelated endpoint). Switching to
the Search API (commits, PRs, repos) sidesteps that entirely: Search respects
normal per-repo permissions, so a properly-scoped read-only fine-grained
token just works — no need for a broader classic token.
Never put a token in config.js or anything that ships to the browser —
only in Vercel's environment variables, which the serverless function reads
server-side. Without it configured, the page still works: it automatically
falls back to the public Events API (public activity only).
- No local runtime needed to preview — it's a static site; any static file
server (or just opening
index.htmldirectly) works. - The two optional serverless functions in
api/(used only when deployed to Vercel) need Node's built-infetch/AbortSignal.timeout, sopackage.jsonpins Vercel's Node runtime to20.x. Bump that if you deploy elsewhere and need a different version — anything ≥18 works.
- The public (unauthenticated) fallback path counts push events, not individual commits — GitHub's Events API no longer returns a commit count on push events (a 2024 privacy change). The authenticated Search API path doesn't have this limitation; it counts real commits.
- GitHub's Search API has its own rate limit (30 requests/min authenticated, 10/min unauthenticated) — plenty for a new-tab page, but worth knowing if you're debugging.
- The contribution heatmap is built from GitHub's own real per-day
contribution data (
api/contributions.jsfetchesgithub.com/users/<user>/contributions, the same public page your profile uses — no token needed), rendered as our own SVG so it can be themed with this app's own tokens. If that endpoint isn't reachable (local static preview, or before the function is deployed), it falls back to the free ghchart.rshah.org image service instead — a third-party dependency, only used as a fallback. - The heatmap mirrors your public GitHub profile graph. If you want private contributions included, enable "Include private contributions on my profile" in your GitHub account settings — the graph (and this heatmap) will then reflect it.
- The recent-activity list caps at 5 items regardless of how much happened
in the
maxDayswindow — it's meant to be a glance, not a full log. - The "Shipped" section header always reads "last year activity" (matching
the heatmap above it), even though the stat row and recent-activity list
below it are windowed to
maxDays— the header describes the heatmap, not the whole section. - The page sets
<meta name="robots" content="noindex, nofollow">since it's meant to be a personal, unlisted URL rather than a discoverable page.
Issues and PRs welcome — this is meant to be a small, hackable template, not a product. If you build a new icon, module, or layout variant that others might want, a PR is very welcome.
MIT — see LICENSE.
