Building VelocityHunt: Discovering GitHub's Rising Stars Before They Trend

February 19, 2026 (4mo ago)

Every developer has experienced this: you discover a library that's perfect for your project, only to realize everyone else found it months ago. By the time a repo hits GitHub Trending, the early adopter window is closed. I wanted a tool that surfaces repos while they're still gaining momentum - before the crowd arrives. That's why I built VelocityHunt.

The Problem with GitHub Discovery

GitHub's built-in Trending page has fundamental limitations:

  1. Lagging indicator - By the time repos trend, the discovery opportunity is gone
  2. No topic filtering - Can't search for trending repos in specific niches
  3. Daily focus - Only shows what's popular today, not what's accelerating
  4. No sharing tools - Finding something cool means manually crafting a post about it
  5. Absolute numbers - A repo with 50,000 stars gaining 10/day isn't "trending" the same way a repo with 200 stars gaining 50/day is

What I wanted:

  • Surface repos with high velocity (growth rate), not just high star counts
  • Filter by topic (AI agents, React libraries, Rust utilities, etc.)
  • One-click sharing to X/Twitter for makers building in public
  • Fast, beautiful interface with zero friction

That's exactly what VelocityHunt does - it finds the repos gaining stars fastest, right now, in topics you care about.

The Tech Stack

I chose a lean, modern stack optimized for speed and deployment simplicity:

Frontend & Framework

  • Next.js 16 with App Router and Server Components
  • React 19 with TypeScript 5
  • Tailwind CSS 4 with a dark-mode-first "spark gold" theme
  • Framer Motion for staggered animations and hover interactions
  • Custom fonts: Geist Sans, Geist Mono, Syne (headings)

Backend & Data

  • GitHub REST API for repository search
  • Supabase (PostgreSQL) for cache persistence
  • Drizzle ORM for type-safe database queries
  • In-memory LRU cache for sub-millisecond repeated queries

UI Components

  • Shadcn/UI style primitives (Badge, Button, Card, Skeleton)
  • Lucide React for icons
  • Sonner for toast notifications

Deployment

  • Vercel for serverless edge deployment
  • ISR (Incremental Static Regeneration) with 1-hour revalidation
  • Bun as package manager

Curious? Try VelocityHunt right now - search any topic and see which repos are gaining momentum fastest. No signup needed.

Architecture Decisions

1. The Spark Score Algorithm (Core Innovation)

The heart of VelocityHunt is a custom scoring algorithm that prioritizes growth rate over absolute popularity. I call it the Spark Score:

// lib/scoring.ts
export function calculateSparkScore(repo: GitHubRepo): number {
  const daysOld = Math.max(
    1,
    daysBetween(new Date(repo.created_at), new Date())
  );
 
  const daysSinceUpdate = daysBetween(
    new Date(repo.updated_at),
    new Date()
  );
 
  // Base velocity: weighted combination of stars and forks
  const baseScore = (repo.stargazers_count * 2 + repo.forks_count) / daysOld;
 
  // Activity penalty: score halves every 30 days of inactivity
  const activityFactor = 1 / (1 + daysSinceUpdate / 30);
 
  // Final score scaled for readability
  return baseScore * activityFactor * 10;
}

Why this formula works:

  • Stars weighted 2x - Stars reflect community interest more directly than forks
  • Forks included - Forks indicate developer adoption (people building with it)
  • Divided by age - A 100-star repo that's 7 days old ranks higher than a 10,000-star repo that's 5 years old
  • Inactivity penalty - Score halves every 30 days without updates, surfacing actively maintained projects
  • Result: Emphasizes "currently accelerating" rather than "historically popular"

Example scoring:

RepoStarsAgeLast UpdateSpark Score
New AI tool50014 days1 day ago71.4
Popular framework45,0002,000 days3 days ago4.5
Abandoned project2,000180 days90 days ago3.7

The new AI tool with 500 stars ranks 16x higher than the popular framework with 45,000 stars, because it's growing much faster relative to its age.

2. Growth Percentage with Recency Boost

Beyond the Spark Score, each repo displays a growth percentage that shows velocity in human-readable terms:

// lib/scoring.ts
export function calculateGrowth(repo: GitHubRepo): number {
  const daysOld = Math.max(
    1,
    daysBetween(new Date(repo.created_at), new Date())
  );
 
  const velocity = repo.stargazers_count / daysOld; // Stars per day
 
  // Repos updated in the last 3 days get a 1.2x boost
  const daysSinceUpdate = daysBetween(
    new Date(repo.updated_at),
    new Date()
  );
  const recencyBoost = daysSinceUpdate <= 3 ? 1.2 : 1.0;
 
  return velocity * 100 * recencyBoost;
}

A repo gaining 5 stars/day that was updated yesterday shows 600% velocity - immediately communicating "this is hot" to users scanning results.

3. Triple-Layer Caching Architecture

GitHub's API has strict rate limits (60 requests/hour unauthenticated, 5,000 with a token). I built a three-layer caching system to serve results fast while staying within limits:

// lib/github.ts
export async function searchRepos(topic: string): Promise<Repo[]> {
  // Layer 1: Memory cache (sub-millisecond)
  const memCached = memoryCache.get(topic);
  if (memCached && !isExpired(memCached, MEMORY_TTL)) {
    return memCached.data;
  }
 
  // Layer 2: Database cache (Supabase, 20-50ms)
  const dbCached = await db.select()
    .from(repoCache)
    .where(eq(repoCache.query, topic))
    .limit(1);
 
  if (dbCached.length > 0) {
    const age = Date.now() - dbCached[0].updatedAt.getTime();
 
    if (age < FRESH_WINDOW) {
      // Fresh: serve immediately
      memoryCache.set(topic, dbCached[0].results);
      return dbCached[0].results;
    }
 
    if (age < STALE_WINDOW) {
      // Stale: serve now, revalidate in background
      revalidateInBackground(topic);
      return dbCached[0].results;
    }
  }
 
  // Layer 3: Fresh fetch from GitHub API
  const fresh = await fetchFromGitHub(topic);
 
  // Write back to both caches
  memoryCache.set(topic, fresh);
  await upsertCache(topic, fresh);
 
  return fresh;
}

Cache timing:

  • Memory cache: 6-hour TTL, max 100 queries (LRU eviction)
  • Database cache: 6-hour fresh window, 24-hour stale window
  • Stale-while-revalidate: Returns cached data instantly while fetching fresh data in the background

Why three layers:

  • Memory handles repeated queries within the same serverless instance (under 1ms)
  • Database handles queries across instances and cold starts (20-50ms)
  • Stale-while-revalidate ensures users never wait for a fresh API call
  • GitHub API is only hit when data is truly stale (>24 hours)

Result: 95%+ of requests served from cache, GitHub API calls reduced to a minimum.

4. Topic-Based Discovery with Curated Suggestions

Rather than making users guess search terms, VelocityHunt provides 9 curated topics plus 6 trending suggestions:

// Curated discovery topics
const TOPICS = [
  'AI Agents',
  'React Libraries',
  'LLM Tools',
  'Next.js Starters',
  'SaaS Boilerplates',
  'Web3',
  'Rust Utilities',
  'Python Automation',
  'Cybersecurity'
];
 
// Trending topics with visual appeal
const TRENDING = [
  { label: 'AI Agents', emoji: '🤖' },
  { label: 'React Libraries', emoji: '⚛️' },
  { label: 'LLM Tools', emoji: '🧠' },
  { label: 'Rust Utilities', emoji: '🦀' },
  { label: 'SaaS Boilerplates', emoji: '🚀' },
  { label: 'Cybersecurity', emoji: '🔒' }
];

Search flow:

  1. User types a topic or clicks a trending suggestion
  2. GitHub API searches for repos created in the last 6 months with that topic
  3. Results filtered to minimum 50 stars (removes noise)
  4. Scored with Spark Score algorithm
  5. Sorted by velocity (highest spark first)
  6. Up to 30 results displayed with staggered animations

The 6-month creation window and 50-star minimum strike the right balance between "new enough to be a discovery" and "established enough to be worth checking out."

5. One-Click Social Sharing

VelocityHunt is built for makers who share their discoveries on X/Twitter. Every repo card has a pre-formatted share button:

// components/RepoCard.tsx
function getShareText(repo: Repo): string {
  return `🔥 Found ${repo.name} on @VelocityHunt\n\n` +
    `⭐ ${repo.stars} stars | ` +
    `📈 ${repo.sparkScore.toFixed(1)} spark score\n\n` +
    `${repo.description}\n\n` +
    `${repo.html_url}\n\n` +
    `Discover trending repos → velocityhunt.vercel.app`;
}
 
// Batch sharing for content creators
function getTop5ShareText(repos: Repo[], topic: string): string {
  const lines = repos.slice(0, 5).map((r, i) =>
    `${i + 1}. ${r.name}${r.stars} (${r.sparkScore.toFixed(1)} spark)`
  );
 
  return `🔥 Top 5 rising ${topic} repos right now:\n\n` +
    lines.join('\n') +
    `\n\nFound on @VelocityHunt → velocityhunt.vercel.app`;
}

Two sharing modes:

  • Per-repo sharing - Tweet about a single interesting repo
  • Batch "Share Top 5" - Aggregate tweet for content creators who curate lists
  • Copy-to-clipboard - For pasting into newsletters, Slack, or Discord
  • Toast feedback - Sonner notifications confirm the action

The self-promoting attribution (Found on @VelocityHunt) builds organic awareness without being intrusive.

6. Glass Morphism UI with Spark Theme

The visual design uses a dark-mode-first glass morphism aesthetic that matches the X/Twitter vibe most users live in:

/* app/globals.css */
.spark-card {
  @apply bg-zinc-900/60 backdrop-blur-xl;
  @apply border border-zinc-800/50;
  @apply rounded-2xl shadow-lg;
  @apply hover:border-amber-500/30 transition-all;
}
 
.spark-meter {
  @apply h-2 rounded-full bg-gradient-to-r;
  @apply from-amber-500 via-orange-500 to-red-500;
}
 
/* Pulse animation for high spark scores */
@keyframes spark-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.7; }
}
 
.high-spark {
  animation: spark-pulse 2s ease-in-out infinite;
}

Design decisions:

  • Spark gold (#fbbf24) as the accent color reinforces the "discovery" metaphor
  • Glass morphism with backdrop-blur-xl creates depth without heaviness
  • Animated spark meter visually communicates velocity at a glance
  • Pulse animation on high-scoring repos draws attention to the hottest finds
  • Staggered Framer Motion animations on the grid create a polished feel

7. Server Components for Data, Client Components for Interaction

I follow a strict separation between server and client components:

// app/page.tsx - Server Component (data layer)
export default async function HomePage({
  searchParams
}: {
  searchParams: Promise<{ q?: string }>
}) {
  const { q: topic } = await searchParams;
 
  return (
    <main>
      <SearchInterface initialTopic={topic} />
      {topic && <RepoResults topic={topic} />}
      {!topic && <TrendingTopics />}
      <Footer />
    </main>
  );
}
 
// RepoResults - Async Server Component
async function RepoResults({ topic }: { topic: string }) {
  const repos = await searchRepos(topic);
 
  if (!repos.length) {
    return <ErrorState message="No repos found" />;
  }
 
  return <RepoGrid repos={repos} topic={topic} />;
}

Server components handle:

  • Data fetching (GitHub API, cache reads)
  • SEO metadata generation
  • ISR page revalidation

Client components handle:

  • Search input with suggestions
  • Repo cards with share buttons
  • Animations and hover interactions
  • Toast notifications
  • Copy-to-clipboard

This separation means the initial page load has zero client-side data fetching - repos are already rendered in the HTML.

Key Challenges & Solutions

Challenge 1: GitHub API Rate Limits

Problem: Without authentication, GitHub allows only 60 requests/hour. Even with a token, 5,000/hour can be hit quickly with popular topics.

Solution: Aggressive multi-layer caching

  • Memory cache serves repeated queries instantly
  • Database cache persists across serverless cold starts
  • Stale-while-revalidate means users never wait for a fresh fetch
  • 6-month creation window and 50-star minimum reduce result sets

Result: 95%+ cache hit rate. Most users never trigger a GitHub API call.

Challenge 2: Scoring Accuracy

Problem: How do you rank repos fairly? A 3-day-old repo with 100 stars shouldn't necessarily beat a 30-day-old repo with 5,000 stars.

Solution: Multi-factor scoring with activity decay

  • Stars weighted 2x over forks (community interest > developer usage)
  • Age normalization (velocity = growth / time)
  • Activity factor penalizes abandoned repos (score halves every 30 inactive days)
  • 50-star minimum filters noise (very new repos need some validation)

Result: The algorithm consistently surfaces repos that developers find genuinely interesting and useful. Repos that make it to the top are usually ones that end up trending on GitHub 2-4 weeks later.

Challenge 3: Search Result Quality

Problem: GitHub's search API returns many irrelevant results, especially for broad topics like "AI."

Solution: Multi-stage filtering

  • Creation date filter: Only repos created in the last 6 months (ensures freshness)
  • Star minimum: 50+ stars filters out personal projects and experiments
  • Topic matching: GitHub's topic system provides decent categorization
  • Scoring sort: Velocity-based sorting pushes the most interesting repos to the top
  • Max 30 results: Curated list > endless scroll

Result: Users typically find 3-5 genuinely interesting repos per search, which is better than scrolling through hundreds of irrelevant results.

Challenge 4: Performance on Vercel Serverless

Problem: Cold starts on serverless functions can add 1-2 seconds to the first request.

Solution: ISR + cache warming

  • ISR with 1-hour revalidation means most page visits are served from static cache
  • Memory cache survives within a single serverless instance
  • Database cache handles cross-instance and cold start scenarios
  • Optimized imports for lucide-react and framer-motion reduce bundle size

Result: Sub-500ms page loads for cached topics, under 2 seconds even on cold starts.

What I Learned

1. Simple algorithms can beat complex ones The Spark Score is just basic math: (stars * 2 + forks) / days * activity_factor. No ML, no embeddings, no vector databases. It works because the core insight (velocity > popularity) is sound. Don't overcomplicate when the fundamentals are right.

2. Caching is a feature, not an optimization The triple-layer cache isn't just about performance - it's about resilience. If GitHub goes down, VelocityHunt still works from cache. If my database goes down, in-memory cache keeps serving. Each layer is a fallback for the one above it.

3. Sharing features drive organic growth The pre-formatted X/Twitter posts with attribution (Found on @VelocityHunt) are the primary growth channel. Making sharing effortless - one click, perfect formatting - turns every user into a potential promoter.

4. Dark mode first matches the audience Developers live in dark mode. Building dark-first with the spark gold accent creates a distinctive identity that resonates with the target audience. Light mode is an afterthought for developer tools.

5. Curated topics > free-form search Offering 9 pre-curated topics and 6 trending suggestions dramatically increased engagement versus just a search box. Users don't always know what to search for - giving them options sparks curiosity.

6. The "minimum viable data" approach works I only store cached search results in the database - no user accounts, no saved lists, no complex schemas. One table (repo_cache) with query + results + timestamp. This simplicity means the entire backend fits in a few hundred lines of code.

Technical Achievements

Here's what shipped:

Discovery:

  • Custom Spark Score algorithm for velocity-based ranking
  • 9 curated topics + 6 trending suggestions
  • Growth percentage with recency boost visualization
  • Animated spark meter for visual velocity communication

Performance:

  • Triple-layer caching (memory + database + stale-while-revalidate)
  • 95%+ cache hit rate reducing GitHub API calls
  • Sub-500ms page loads for cached topics
  • ISR with 1-hour revalidation

Sharing:

  • Per-repo X/Twitter share with pre-formatted text
  • Batch "Share Top 5" for content creators
  • Copy-to-clipboard with toast feedback
  • Self-promoting attribution for organic growth

Design:

  • Dark-mode-first glass morphism aesthetic
  • Spark gold theme (#fbbf24) with animated accents
  • Staggered Framer Motion animations
  • Responsive skeleton loading states

Try It Yourself

Ready to discover the next big repo before everyone else? Check out VelocityHunt - just search a topic and see what's gaining momentum right now. No signup, no paywall, instant results.

Whether you're scouting for new tools, looking for project inspiration, or building content about emerging repos, VelocityHunt surfaces what matters before the crowd arrives.

Share your best finds on X - I'd love to see what you discover.

What's Next

I'm thinking about:

  • Email digests - Weekly emails with the top sparks in your chosen topics
  • Historical tracking - See how a repo's velocity changed over time
  • Comparison mode - Compare two repos side by side
  • RSS feeds - Subscribe to topics via your favorite feed reader
  • Browser extension - See spark scores directly on GitHub repo pages

VelocityHunt started as a weekend project to scratch my own itch, and it's become a daily tool I use myself to stay ahead of trends. The best products are the ones you'd build even if nobody else used them.


Want to build your own developer tool or need help with Next.js, caching strategies, or API optimization? I'm available for consulting and freelance projects. Get in touch.