Building Tutorial Tinder: from idea to deployed Replit demo

Building Tutorial Tinder: from idea to deployed Replit demo

I've always been frustrated by how hard it is to discover good coding projects to learn from. GitHub has millions of repositories, but finding one that's actually beginner-friendly *and* runs without hours of setup feels like searching for a needle in a haystack. I wanted something that made discovery feel fun instead of exhausting—something that got me building in seconds, not hours.

So I built Tutorial Tinder: swipe through curated GitHub repos like you're browsing Tinder, and launch anything interesting directly into Replit with one click. The repos are hand-picked, the setup is automatic, and you go from "that looks cool" to "it's running in my browser" in about 10 seconds.

This post walks through the entire journey—from the first prompt I gave an AI assistant to the production bugs I had to fix at 9 PM (oops accidentally forgetting about OAuth). I'll show you the prompts I used, the architecture decisions I made (and why), the challenges that stumped me, and what I'd build differently with more time. By the end, you'll understand not just *what* I built, but *how* to build something similar yourself.

The problem I wanted to solve

Let me paint a picture: you're a week into learning to code, you've done some tutorials, you understand the basics, and now you want to tinker with a real project. So you go to GitHub, search for "beginner JavaScript project," and get... 50,000 results.

You click on one. It looks promising! But the README assumes you know what npm is. And there's a `.env.example` file with 12 different API keys you need to set up. And the last commit was 3 years ago. And when you finally get it running locally, it crashes because you have the wrong version of Node installed.

This happens over and over. Setup hell kills motivation. Even experienced developers waste hours configuring environments instead of actually learning.

I wanted to fix this. I wanted an app that:

  • Shows you only repos that are actually beginner-friendly
  • Makes browsing feel fast and fun (no endless scrolling)
  • Launches repos in Replit so they just work—no local setup required
  • Gets you from "browsing" to "experimenting" in seconds

The swipe interface came from a simple insight: discovery paralysis happens when you have too many choices and no clear way to filter. Tinder solved this for dating by making the interface fast and decisive. Why not apply the same concept to code?

How it works

Here's what happens when you use Tutorial Tinder:

  1. Open the app → You see a stack of repo cards
  2. Swipe left → Skip repos that don't interest you
  3. Swipe right → Save repos to check out later
  4. Click "Launch in Replit" → The repo opens in Replit, ready to run

Each card shows you the essentials: repo name, description, primary language, star count, and a preview of the README. No noise, just the information you need to decide if it's worth trying.

Behind the scenes, there's a lot more happening. The app pulls from curated sources (my GitHub starred lists, Reddit's r/coolgithubprojects), checks if repos will actually run on Replit, and can even auto-generate cleaned-up templates for repos that are a mess. But as a user, you don't see any of that complexity. You just swipe, click, and code.

Setup instructions

Prerequisites

  • Replit account (free tier works)
  • GitHub account (for importing repos and optionally connecting your starred lists)

Running on Replit

  1. Fork or import this repo into Replit
  • Visit the Tutorial Tinder Repl
  • Click "Fork" to create your own copy

2.  Click "Run"

  • Replit will auto-install dependencies and start the Next.js dev server
  • The app will be live at your Repl's preview URL
  1. Start swiping!

Running locally

If you want to run this outside Replit:

  1. Clone the repo
git clone https://github.com/erinmikailstaples/tutorial-tinder
cd tutorial-tinder

  1. Install dependencies
npm install
  1. Set up environment variables
cp .env.example .env.local
  1. Start the dev server
npm run dev
  1. Open the site
http://localhost:3000

Building it: from concept to code

The "aha" moment

I kept thinking: what's Replit's superpower? It's that you can go from seeing an interesting repo to running code in your browser in about 10 seconds. No git clone, no npm install, no "wait, which version of Python do I need?" Just click and go.

The problem is, nobody knows about this superpower until they try it. So my goal became: **How do I get more people to experience that magical moment?**

The answer hit me when I was mindlessly swiping through a different app: **What if launching a project on Replit felt as easy as swiping right on Tinder?** Match the "swipe → instant gratification" dopamine loop with Replit's "import → instant run" superpower.

The user persona I kept in mind while building this —someone I'll call Aaron. Aaron is a week into learning to code, feels overwhelmed by GitHub, and just wants to find something approachable that runs right away. No complex setup, no reading 3000 words of documentation before writing a single line. Just: see it, click it, run it, modify it, learn from it.

(Yes, Aaron was actually me in my early-to-building journey)

That user story, building the tool that I wanted when I was starting out,  guided every decision I made.

Choosing the tech stack

My goal was to build this in an afternoon, within 3 to 4 hours. (Constraints bring creativity, okay?) That meant a lot of my time (actually ~35 minutes of it to be exact) was simply planning out the project.  Does this seem like a long time to write some prompts? Well maybe—but at the end of the day, I truly believe it's this pre-work that makes “vibe-coding” more realistic. 

early ideating with ChatPRD

I dumped some notes into an obsidian doc, pasted it into my BFF chatprd.ai and had the start of a PRD (Product Requirements Document) that I could then use to scaffold out this application in Replit.

Thank you ChatPRD for validating my ideas.

So I needed tools that were fast to work with but wouldn't create technical debt.

Here's what I landed on:

Next.js (App Router) + TypeScript became the foundation. Replit has excellent Next.js support, API routes let me keep GitHub tokens server-side (security!), and TypeScript catches my dumb mistakes before they become production bugs. Plus, serverless API routes meant I didn't need to spin up a separate backend—everything could live in one codebase.

Tailwind CSS for styling because I can throw together a decent-looking UI in minutes without leaving my JSX. shadcn/ui for components because why build a modal from scratch when someone's already done it better?

TanStack Query for data fetching because it handles caching, loading states, and error states automatically. I just tell it what to fetch and it figures out the rest.

simple-git and Octokit for the heavy lifting: cloning repos, manipulating files, and creating new GitHub repos. These libraries let me do sophisticated git operations from Node.js, which became crucial for the template generation feature I added later.

The entire stack runs on Replit with zero configuration. I literally clicked "Create Repl" → and started coding. No Docker, no deployment pipeline, no "works on my machine" debugging.

Tutorial Tinder's first prompt (they grow up so fast...)

The first big architectural decision

Early on (as in, Replit's first response to me), I faced a choice: should I build live GitHub search with ML-powered ranking, or just use curated lists?

For the sake of simplicity, it felt safer to go with curated lists.  Github is a large site with over a million repos, there was no way I could safely determine which ones make sense within a given timeframe. 

So I went with curated lists and it turned out to be the right call:

  • Faster to build — No complex algorithms, no ML models, just fetch from a list
  • Better quality — Every repo is hand-picked. No "this repo has 10k stars but hasn't been updated in 4 years" noise
  • More interesting integrations — Instead of building one algorithmic search, I pulled from multiple sources: my GitHub starred lists, Reddit's r/coolgithubprojects community, and a custom collection of MCP projects

The tradeoff? Less serendipitous discovery.

But for a demo meant to showcase Replit's capabilities, quality beats quantity every time. Plus, building multiple data source integrations (GitHub + Reddit) turned out to be a better technical showcase than a ranking algorithm would've been.

Where the repos come from

I integrated three different sources, each with its own personality:

  1. My GitHub starred lists — Repos I've personally vetted for being beginner-friendly and Replit-compatible. Two lists: General Tutorials and MCP projects
  2. Reddit's r/coolgithubprojects — Community-upvoted projects that people are excited about *right now*. This adds freshness and variety
  3. Custom picks — Projects I specifically chose because I knew they'd showcase different frameworks and run smoothly on Replit

Each source gets a short description so users know what kind of projects they're browsing. Want battle-tested tutorials? Pick the curated list. Want to see what the community is excited about today? Check Reddit.

Technical implementation

Frontend: Next.js + swipe UI

The frontend is built with Next.js 14 (App Router) and organized into these key routes:

  • / — Landing page with hero and "Start Swiping" CTA
  • /deck — Main swipe interface (card stack)
  • /saved — List of saved repos

Swipe mechanics

The card stack UI supports:

  • Touch gestures — Swipe left to skip, swipe right to save
  • Keyboard shortcuts — `←` to skip, `→` to save, `Enter` to launch
  • Click actions — Buttons for "Skip", "Save", "Launch in Replit"

Each card displays:

  • Repo name, owner, description
  • Primary language, star count, last updated date
  • Short README preview (first ~300–500 characters)
  • "Convert to Replit Template"
  • "Launch as-is in Replit"
  • "View on GitHub"

Key component code (simplified):

// components/RepoCard.tsx
export function RepoCard({ repo, onSwipe }: RepoCardProps) {
  return (
    <div className="card" data-repo={repo.id}>
      <h2>{repo.name}</h2>
      <p>{repo.description}</p>
      <div className="metadata">
        <Badge>{repo.language}</Badge>
        <span>⭐ {repo.stars}</span>
      </div>
      <ReadmePreview content={repo.readme} />
      <div className="actions">
        <Button onClick={() => onSwipe('left')}>Skip</Button>
        <Button onClick={() => onSwipe('right')}>Save</Button>
        <Button onClick={() => launchInReplit(repo)}>
          Launch in Replit
        </Button>
      </div>
    </div>
  );
}

Backend: API routes and GitHub integration

All backend logic lives in Next.js API routes under /app/api/:

  • /api/repos - fetch curated repos

Returns a list of repos from the selected source (tutorials, MCP, Reddit, etc.).


// app/api/repos/route.ts
export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const source = searchParams.get('source') || 'tutorials';
  
  const repos = await fetchReposFromSource(source);
  return NextResponse.json({ repos });
}

For GitHub starred lists, I use the Octokit client with a server-side token:

import { Octokit } from '@octokit/rest';

const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });

async function fetchReposFromSource(source: string) {
  if (source === 'tutorials') {
    const { data } = await octokit.activity.listReposStarredByUser({
      username: 'erinmikailstaples',
      per_page: 50,
    });
    return data.map(normalizeRepo);
  }
  // ... handle other sources
}

For Reddit, I fetch posts from r/coolgithubprojects via Reddit's API, parse GitHub URLs from post bodies, then fetch repo metadata:

async function fetchFromReddit() {
  const response = await fetch(
    'https://www.reddit.com/r/coolgithubprojects/hot.json',
    { headers: { 'User-Agent': 'tutorial-tinder/1.0' } }
  );
  const { data } = await response.json();
  
  const githubUrls = data.children
    .map(post => extractGitHubUrl(post.data.url))
    .filter(Boolean);
    
  return Promise.all(githubUrls.map(fetchRepoMetadata));
}

/api/preflight - Validate repos before launch

Before launching a repo into Replit, I added a preflight check that:

  1. Clones the repo to a temp directory (shallow clone)
  2. Detects language/framework by inspecting files:
    1. Node.js: looks for package.json, reads scripts
    2. Python: looks for requirements.txt, pyproject.toml, main.py, app.py
  3. Infers a run command (e.g., npm run dev, python app.py)
  4. Identifies potential issues (missing dependency files, archived repo, etc.)
  5. Returns metadata with a confidence score (0–1)

Preflight endpoint code:

// app/api/preflight/route.ts
import { mkdtempSync, existsSync, readFileSync } from 'fs';
import { tmpdir } from 'os';
import simpleGit from 'simple-git';

export async function POST(req: Request) {
  const { owner, repo, defaultBranch = 'main' } = await req.json();
  
  const tmp = mkdtempSync(path.join(tmpdir(), 'preflight-'));
  const git = simpleGit(tmp);
  
  await git.clone(
    `https://github.com/${owner}/${repo}.git`,
    '.',
    ['--depth', '1', '--branch', defaultBranch]
  );
  
  const config = detectLanguageAndRunCommand(tmp);
  
  return NextResponse.json({
    language: config.language,
    framework: config.framework,
    runCommand: config.runCommand,
    issues: config.issues,
    confidence: config.confidence,
  });
}

function detectLanguageAndRunCommand(root: string) {
  const hasPackageJson = existsSync(path.join(root, 'package.json'));
  const hasMainPy = existsSync(path.join(root, 'main.py'));
  
  if (hasPackageJson) {
    const pkg = JSON.parse(
      readFileSync(path.join(root, 'package.json'), 'utf8')
    );
    const scripts = pkg.scripts || {};
    const runCommand = scripts.dev
      ? 'npm install && npm run dev'
      : 'npm install && npm start';
    
    return {
      language: 'nodejs',
      framework: detectJsFramework(pkg),
      runCommand,
      issues: [],
      confidence: 0.9,
    };
  }
  
  if (hasMainPy) {
    return {
      language: 'python',
      framework: null,
      runCommand: 'pip install -r requirements.txt && python main.py',
      issues: [],
      confidence: 0.85,
    };
  }
  
  return {
    language: 'unknown',
    framework: null,
    runCommand: 'echo "Please see README for setup"',
    issues: ['Could not detect language or entrypoint'],
    confidence: 0.3,
  };
}

Why this matters: Many repos don't run out-of-the-box on Replit. Preflight checks let me warn users or auto-generate a cleaned template.

/api/template - Generate Replit-ready templates

For repos with low confidence or known issues, I built a template generator that:

  1. Clones the source repo
  2. Detects language, framework, and run command (same as preflight)
  3. Strips unnecessary files (.github, .git, node_modules, pycache, .vscode, .idea, tests, docs)
  4. Overwrites `README.md` with a "Start Here" guide
  5. Creates a `.replit` config file with the detected run command
  6. Pushes the cleaned template to a new GitHub repo (under a bot account)
  7. Returns the new template's Replit import URL

Replit Integration: 1-click launch

The core value prop is launching a repo in Replit with a single click. Replit supports importing GitHub repos via:

https://replit.com/github/<owner>/<repo>

When the user clicks "Launch in Replit":

  1. Frontend calls `/api/preflight` with repo info
  2. If `confidence >= 0.8`, open the original repo URL
  3. If `confidence < 0.8`, offer to generate a template first
  4. On template generation, call `/api/template` and open the returned `replitImportUrl`
  5. Show a toast: "Launching in Replit… go build!"

Prompts used during development

A key part of this project was working with AI coding assistants, namely Replit. Here are the prompts I used:

Prompt: Initial project scaffolding

Yes, this prompt was LONG — but I feel that this detail helped in the long-run. I used ChatPRD to help me come up with this and went back and forth a few times to best outline the prompt for my needs, and used my previous history of software development to best tailor it to where I know these apps tend to get stuck.

You are creating a Replit demo project called **Tutorial Tinder** (aka Developer Tinder).

Goal:
Build a swipeable web app that helps beginner developers discover GitHub repos and 1-click launch them into Replit. This should feel fun (Tinder-style swipe UI) but also be a serious, educational demo that shows off why Replit is great for getting from "repo" → "running project" fast.

Tech stack:
- Next.js (App Router) + TypeScript
- Tailwind CSS for styling
- shadcn/ui for cards, buttons, modals
- TanStack Query for data fetching/caching
- API routes to proxy GitHub REST Search API 



Core features (must have):
1. **Landing page (`/`)**
   - Simple hero explaining: "Swipe through beginner-friendly GitHub repos and 1-click launch them in Replit."
   - CTA button: "Start Swiping" → `/deck`.

2. **Deck page (`/deck`) – Tinder-style interface**
   - Card stack UI:
     - Swipe right / or click "Save"
     - Swipe left / or click "Skip"
     - Optional "Launch in Replit" primary button on card
   - Keyboard shortcuts:
     - ← = skip
     - → = save
     - Enter = launch
   - Each card shows:
     - Repo name, owner, description
     - Primary language, stars, last updated
     - Difficulty tag (e.g., Beginner / Intermediate) inferred from topics / stars / description
     - Short README preview (first ~300–500 chars)
     - Button: "View on GitHub"
     - Button: "Launch in Replit"

3. **Saved repos (`/saved`)**
   - Simple list/table of repos the user swiped right on (stored in localStorage is fine for v1).
   - Each entry: minimal metadata + "Launch in Replit" + "Remove".

4. **GitHub search + backend**
   - API route: `/api/search`
     - Calls GitHub REST Search API: `/search/repositories`
     - Query tuned for beginner-friendly repos:
       - Topics like: `tutorial`, `starter`, `example`, `good-first-issue`
       - `stars:>10`
       - Sorted by `updated` or `stars`
     - Accepts filters as query params: `language`, `difficulty`, `page`.
     - Returns normalized JSON used by the frontend.
   - Basic error handling and a user-friendly error state:
     - "GitHub rate limit hit. Try again in a few minutes."
   - **Security:** do NOT expose tokens in the client. Use env vars on the server.

5. **Replit launch integration**
   - For each repo, generate a launch URL that opens the repo in Replit.
   - Use the GitHub -> Replit import URL pattern:
     - e.g. `https://replit.com/github/<owner>/<repo>` (or whatever the current Replit import URL pattern is).
   - Button "Launch in Replit":
     - Opens in a new tab
     - Show in-app toast: "Launching this repo in Replit… go build!"

6. **Run hints (simple version)**
   - On the backend, inspect repo data (filename heuristics only, no full clone needed):
     - If repo language is JavaScript/TypeScript and has `package.json`, suggest `npm install && npm run dev` or `npm start`.
     - If Python and contains `main.py` or `app.py`, suggest `python main.py` or `python app.py`.
   - Show "Suggested run command" in the card or details modal.

7. **README**
   - `README.md` in root must include:
     - What Tutorial Tinder is and why it's compelling for Replit builders.
     - How to run locally on Replit (clear steps).
     - How the GitHub search + swipe deck is implemented (high level).
     - How this demo showcases Replit's strengths:
       - Fast from idea → running app
       - Great for beginners launching repos with zero setup
     - Notes on what you would add/improve with more time (e.g., auth, real collections, better difficulty scoring).

Non-goals (to keep scope tight):
- No auth beyond localStorage for saves (v1).
- No full GitHub client (issues/PRs editing, etc.).
- No social features (comments, sharing) in v1.

UX details:
- Make it feel fun but fast:
  - Smooth card animations.
  - Skeleton loaders while fetching.
  - Toasts for errors, saves, and launches.
- Keep copy very beginner-friendly, but code and structure clean enough that advanced devs can read and learn from it.

Focus:
This should be a polished, educational Replit demo that:
- Shows off a real integration with an external API (GitHub).
- Makes it obvious why Replit is perfect for "see cool repo → instantly run it."
- Is easy for other builders to fork and extend on Replit.

Prompt: Switching to curated lists

I would like to pull from a curated list of beginner-friendly repositories that I'll provide.

Most importantly i want people to be able to open the repositories in Replit and get a successful launch in replit.

Here's some additional information about the tech stack as well as the interface

Tech stack:

- Next.js (App Router) + TypeScript
- Tailwind CSS for styling
- shadcn/ui for cards, buttons, modals
- TanStack Query for data fetching/caching
- API routes to proxy GitHub REST Search API (no tokens on client)

Core features:

1. **Landing page (`/`)**
    
    - Simple hero explaining: "Swipe through beginner-friendly GitHub repos and 1-click launch them in Replit."
    - CTA button: "Start Swiping" → `/deck`.
2. Card stack UI:
    
    - Swipe right / or click "Save"
        
    - Swipe left / or click "Skip"
        
    - "Launch in Replit" primary button on card
        
    - Keyboard shortcuts:
        
        - ← = skip
        - → = save
        - Enter = launch
        
        Each card shows:
        
        - Repo name, owner, description
        - Primary language, stars, last updated  
            Short README preview (first ~300–500 chars)
        - Button: "View on GitHub"
        - Button: "Launch in Replit"
        
        Replit launch integration**
        
    - For each repo, generate a launch URL that opens the repo in Replit.
        
    - Use the GitHub -> Replit import URL pattern:
        
        - e.g. `https://replit.com/github/<owner>/<repo>` (or whatever the current Replit import URL pattern is).
    - Button "Launch in Replit":
        
        - Opens in a new tab
        - Show in-app toast: "Launching this repo in Replit… go build!"

Is there anything that would make this difficult in Replit or that I should be aware of when building?

Prompt: Adding multiple lists

Add more of the following lists and projects: 

- https://github.com/stars/erinmikailstaples/lists/tutorials
- allow to sort from random projects from reddit's cool github projects https://www.reddit.com/r/coolgithubprojects/
- https://github.com/stars/erinmikailstaples/lists/mcp

Add a blurb describing the types of projects in each.

Prompt: Reddit Integration + caching bugs

I also caught a frontend caching quirk here when testing the app, combining prompts can be risky, but given it was seemingly small, I went for it.

Can you please update to achieve this: Option A: Implement the Reddit integration now to fetch trending repos from r/coolgithubprojects as a fourth list

As well as please look into the frontend caching quirk

Prompt: Preflight checks

I noticed when launching the replit apps, it needed some extra help and standard github repos weren't always working. A dive into the replit documentation and guidance from the Replit AI Agent helped me guide this project towards it's success.

Build the following changes into the project:

1. Add a new API route: POST /api/preflight
   - Backend clones the repo to a temp directory (shallow clone).
   - Detect language/framework:
     - Node: detect package.json, scripts, dependencies (Next.js, React, Vite).
     - Python: detect requirements.txt, pyproject.toml, main.py/app.py/manage.py, frameworks like Flask/FastAPI/Django.
   - Infer run command:
     - Node: "npm install && npm run dev" or "npm start" depending on scripts.
     - Python: "pip install -r requirements.txt && python app.py/main.py".
   - Identify potential issues (missing deps file, missing entrypoint, archived repo, etc.).
   - Return JSON: { language, framework, runCommand, issues: [], confidence: 0–1 }

2. On swipe-right or "Launch":
   - Frontend calls /api/preflight first.
   - Show a modal/toast summarizing:
     - Detected language
     - Suggested run command
     - Confidence score
     - Any warnings

3. If confidence >= 0.8:
   - Launch original repo to Replit as usual.

4. OPTIONAL (add scaffolding for this): Add a second backend endpoint POST /api/template that:
   - Takes repo info and the preflight data.
   - Cleans the repo: remove .github, .git, node_modules, __pycache__, .vscode, etc.
   - Writes a new README.md ("Start Here" with instructions).
   - Writes a .replit file:
     run = "<DETECTED_RUN_COMMAND>"
     [interpreter]
     language = "<DETECTED_LANGUAGE>"
   - Initializes a new git repo, commits, and pushes to a bot-owned GitHub org using PAT.
   - Returns: { templateRepoUrl, replitImportUrl }

5. In the frontend:
   - If confidence < 0.5, give user options:
     - Launch anyway
     - Skip
     - (Later) Try template generation
   - If using template generation, open the returned replitImportUrl instead of the original repo.

6. Keep everything in the existing Next.js + TypeScript + API routes structure. Use simple-git and @octokit/rest for backend logic. Use React Query for preflight requests.

Goal: Make sure repos launched from the app reliably run on Replit by adding preflight detection + optional auto-configuration. Keep the implementation clean and incremental—start with preflight, then layer in template generation logic.
💡
A note on pricing feedback: One thing I wish Replit or the AI assistant would tell me upfront is the potential costs or charges associated with the different tradeoffs, what might be more cost effective in the long-run to build.

Understanding pricing models is important when building demos that could scale.

Prompt: Replit Template Generation

I was initially having trouble with the application generating Replit templates and needed to do some further prompting, here's how I guided the application:

 have an existing Replit app that already:
- Fetches GitHub repos and shows them in a swipe UI.
- Can launch a repo on Replit via: https://replit.com/github/<owner>/<repo>
- has a /api/preflight endpoint that returns repo metadata (owner, name, defaultBranch, language guesses, etc.).

I want to add a **template conversion flow** so that, given an existing repo, the app can create a **Replit-friendly template** and launch THAT instead.

Please implement the following:

1. Create a new backend endpoint:
   - Route: POST /api/template
   - Request body (JSON):
     {
       "owner": string,
       "repo": string,
       "defaultBranch"?: string
     }
   - It can optionally accept extra hints like language or runCommand from the existing preflight endpoint, but it must also work if only owner/repo/defaultBranch are provided.

2. In /api/template, do this:
   - Use simple-git (or equivalent) to:
     - Create a temporary directory.
     - Shallow-clone the repo:
       git clone --depth 1 --branch <defaultBranch or main> https://github.com/<owner>/<repo>.git
   - Inspect the cloned repo to infer:
     - language: "nodejs" | "python" | "other"
     - framework (if obvious): "nextjs" | "react" | "vite" | "flask" | "fastapi" | "django" | null
     - runCommand:
       - If package.json exists:
         - If scripts.dev: "npm install && npm run dev"
         - Else if scripts.start: "npm install && npm start"
         - Else: "npm install && npm run dev"
       - Else if Python:
         - If app.py: "pip install -r requirements.txt && python app.py"
         - Else if main.py: "pip install -r requirements.txt && python main.py"
         - Else: "pip install -r requirements.txt && python main.py"
       - Else fallback: "echo 'Please see README.md for instructions'"
   - Strip unnecessary files to make a clean template:
     - Try to remove: .github, .git, .vscode, .idea, node_modules, dist, build, __pycache__, tests, test, docs
     - Use recursive deletion with force; ignore errors if files/folders don't exist.
   - Overwrite or create a new README.md in the root that:
     - States this is a Replit-ready starter generated from <owner>/<repo>.
     - Documents how to run the project on Replit using the inferred runCommand.
     - Briefly explains what the template generator did (detect language, clean files, add config).
   - Create or overwrite a .replit file in the root with content similar to:

     run = "<DETECTED_RUN_COMMAND>"

     [interpreter]
     language = "<DETECTED_LANGUAGE>"

     where DETECTED_LANGUAGE is "nodejs" or "python" or "bash" as appropriate.

   - Initialize a new git repo in this cleaned directory:
     - git init
     - git add .
     - git commit -m "chore: generate Replit-friendly template from <owner>/<repo>"

   - Use @octokit/rest and a GitHub Personal Access Token stored in an env var (e.g. TEMPLATE_BOT_TOKEN) to:
     - Create a new public repo in a configured org or user (env var TEMPLATE_ORG), with a name like:
       "<source-repo>-replit-template-<timestamp>"
     - Add that new repo as remote origin.
     - Push the local main branch to the new repo.

   - Respond with JSON:
     {
       "templateRepoUrl": "https://github.com/<org>/<slug>",
       "replitImportUrl": "https://replit.com/github/<org>/<slug>"
     }

3. Update the frontend:
   - For each repo card, add a "Convert to Replit Template" or "Launch as Template" action.
   - On that action:
     - Call POST /api/template with the repo's owner, name, and defaultBranch.
     - Show a loading toast: "Generating Replit-ready template…"
     - On success:
       - Show success toast: "Template created! Launching in Replit…"
       - Open the returned replitImportUrl in a new tab (window.open(url, "_blank", "noopener")).
     - On error:
       - Show error toast: "Template generation failed. Launching original repo instead."
       - Fall back to launching https://replit.com/github/<owner>/<repo>.

4. Keep everything in TypeScript and within the existing Next.js app/api structure. Make sure:
   - No GitHub tokens or secrets are sent to the client.
   - Temp directories are cleaned up after use.
   - Errors in cloning or pushing are caught and returned as proper 4xx/5xx responses with helpful error messages.

The end result should be:
- I can pick any repo surfaced by my app,
- Click a "Convert to Replit Template" / swipe-right,
- And the app will:
  - Clone it,
  - Infer language + run command,
  - Clean it,
  - Add README + .replit,
  - Push to a template repo,
  - And launch that template on Replit.
💡
Working with longer prompts: when using AI code-assist tools, I find it helpful to open up the code in a code editor of your choice and start reviewing it manually, then piecing items together so the AI code tool can pick up the patterns in the existing code base.

I have started keeping a log of different prompts I've used our outlines inside of a folder in my obsidian workspace for future references.

Challenges and solutions

Challenge 1: Frontend caching quirk

Context: After fetching new repos, the card deck sometimes showed stale data.

Symptom: Users would switch between lists (tutorials → MCP → Reddit) and see cached repos from the previous list.

Root cause: TanStack Query's default staleTime was too aggressive, and I wasn't invalidating queries when switching lists.

Fix: Updated the query key to include the source, and set a shorter staleTime:

export function useRepos(source: string) {
  return useQuery({
    queryKey: ['repos', source], // ← source in key
    queryFn: () => fetch(`/api/repos?source=${source}`).then(r => r.json()),
    staleTime: 1000 * 60 * 2, // ← reduced from 5min to 2min
  });
}

Challenge 2: Reddit API rate limits

ContextL Reddit's API has strict rate limits and requires a User-Agent header.

Symptom:  Fetching from r/coolgithubprojects worked locally but failed intermittently in production.

Root Cause: Missing User-Agent header and hitting Reddit's rate limit.

Fix: Added proper headers and implemented exponential backoff:

async function fetchFromReddit() {
  const response = await fetch(
    'https://www.reddit.com/r/coolgithubprojects/hot.json',
    {
      headers: {
        'User-Agent': 'tutorial-tinder/1.0 (contact@example.com)',
      },
    }
  );
  
  if (!response.ok) {
    if (response.status === 429) {
      throw new Error('Reddit rate limit hit. Try again in a few minutes.');
    }
    throw new Error(`Reddit API error: ${response.status}`);
  }
  
  return response.json();
}

Also added caching on the server side (5-minute TTL) to reduce Reddit API calls.

Challenge 3: Template generation workflow errors

Context: Creating new GitHub repos via Octokit sometimes failed with cryptic errors.

Symptom: Error: "Not Found" when trying to create repos in an organization.

Root Cause:The GitHub PAT didn't have repo and admin:org scopes.

Fix: Regenerated with correct scopes and improved error messages

Challenge 4: GitHub API queries for Reddit posts

Context: Reddit posts link to GitHub repos, but I needed to fetch repo metadata from GitHub.

Symptom: Had Reddit post URLs but no repo stars, language, or README previews.

Solution:  Parse GitHub URLs from Reddit post bodies, then fetch metadata. This was uniquely possible in this scenario because the subreddit r/coolgithubproject only allows link post types where the body of the post is always a github url.

function extractGitHubUrl(url: string): string | null {
  const match = url.match(/github\.com\/([^\/]+)\/([^\/]+)/);
  return match ? `${match[1]}/${match[2]}` : null;
}

async function fetchRepoMetadata(fullName: string) {
  const [owner, repo] = fullName.split('/');
  const { data } = await octokit.repos.get({ owner, repo });
  return normalizeRepo(data);
}

This let me merge Reddit's community curation with GitHub's technical metadata.

Challenge 5: Template generation git branch issues

Context: When generating Replit-ready templates, the repos were being created on GitHub but files weren't showing up in Replit.

Symptom: 

  • Template repos were created successfully
  • GitHub showed an empty repository
  • Replit import failed or showed no files
  • Example generated repos:
    • https://github.com/erinmikailstaples/startup-sim-3000-replit-template-1763081115091
    • https://replit.com/github/erinmikailstaples/startup-sim-3000-replit-template-1763081115091

Root Cause: The repository was being created on GitHub with `master` as the default branch, but the template generation was pushing to `main`. This meant files went to the `main` branch, but when viewing the repo or importing it into Replit, it showed the empty `master` branch.

Additionally, the order of git operations was wrong: trying to rename the branch BEFORE making the first commit. When you do `git init`, it doesn't actually create a branch until you make your first commit.

Additional notes from development:

During development of this application, I took note of different key iterations and fixes:

From early architecture decisions:

Template generation adjustments:

UI Improvements for alerts:

And even using the Replit AI chatbot to debug the template generator:

💡
COOL FEATURE ALERT: Replit's queue feature was nice when I'd catch an error early on in the process or was testing something else out in the build, and could simply queue a fix for the agent to resolve.

Results and learnings

What works well

  1. The swipe UX is genuinely fun — Friends I quickly shared it with  said it felt addictive and way faster than browsing GitHub directly
  2. Replit's 1-click import is magic — Going from card to running code in ~10 seconds blew me away and I was even shocked how fast it worked 
  3. Template generation saves time — Auto-cleaning repos and adding `.replit` configs turned "this won't run" into "it just works"

How this shows off Replit's potential

This demo highlights several of Replit's strengths:

  1. Zero-to-running in seconds — No local setup, no environment configuration, no "works on my machine" issues
  2. Collaboration-ready — Every launched repo becomes a shareable Repl
  3. Beginner-friendly — The `.replit` config and Nix packages mean newcomers don't need to understand toolchains
  4. Rapid prototyping — I built this entire demo *on Replit* in under 4 hours, using Replit AI to scaffold components and debug issues
  5. Production-ready deploys — The same app that runs in dev is one click away from a public URL

What surprised me

  • Standardizing Template generation was harder than I thought — Detecting languages and run commands is easier than I expected, but handling edge cases (monorepos, unconventional setups) is much harder
  • Replit AI is great at scaffolding — For boilerplate components and API routes, it saved me hours
  • Curated > algorithmic for demos — I initially wanted live search with some sort of ranking, but hand-picked lists provided a better first-run experience

Future improvements

With more time, here's what I'd add:

User accounts and persistent saves

State at initial publishing: Saved repos live in localStorage.

Improvement:  Add GitHub OAuth to persist saves across devices and browsers.
This could also let users:

  • Create custom collections ("My React Projects", "Python CLI Tools")
  • Share collections with others
  • Track launch history

Note: started working on this beyond the 4-hour time constraint

Advanced filtering

Current State:  Users pick a list (tutorials, MCP, Reddit).

Improvement: Add filters for:

  • Language (JavaScript, Python, Go, Rust, etc.)
  • Framework (Next.js, Flask, FastAPI, etc.)
  • Difficulty (Beginner, Intermediate, Advanced)
  • Star count range
  • Last updated (past week, month, year)

Analytics dashboard

Track:

  • Launch success rate (did the Repl run without errors?)
  • Most popular repos
  • Average time from swipe to launch
  • User engagement (saves per session, return rate)

This data would help refine curation and improve accuracy of the template generation.

Community-created lists

Let users create and share their own curated lists. This could turn into a marketplace of "best repos for learning X" or "projects for hackathons."

Mobile polish

The swipe UI works on mobile, but the cards are optimized for desktop. I'd add:

  • Better touch gesture feedback
  • Smaller card text for mobile screens
  • Pull-to-refresh for fetching new repos

Production template generation

Current state: Templates are created on-demand and pushed to the users github

Improvement:* Pre-generate templates for popular repos and cache them. This would:

  • Reduce API load
  • Speed up launch times
  • Allow for manual curation and quality checks

Also, instead of creating new repos dynamically, I could use GitHub Gists or Replit Templates directly.

Conclusion

Building Tutorial Tinder was a crash course in what makes Replit special. The ability to go from idea → deployed demo in a few hours, quickly iterate with AI-assisted coding, and share a working app with a URL is genuinely transformative for builders.

This project solves a real problem: helping new developers discover projects they can actually run and learn from, without getting stuck in setup hell. And it does so in a way that's fun, fast, and showcases Replit's core value proposition.

Try the Demo: Tutorial Tinder on Replit

Fork the code: Check out the GitHub repo

Tell me what to improve: I'd love to hear your thoughts! Open an issue, send a PR, or DM me your favorite repos to add to the curated lists.

Thanks for reading, and happy swiping! 🚀