DEV Community

윤병준
윤병준

Posted on

I Built 68 Free Online Tools in 2 Weeks with Next.js — Here's the Architecture

A frontend developer with a 4-hour daily commute, two young kids, and zero free time decided to build passive income anyway.

Table of Contents

  1. Why a guy with no free time started a side project
  2. The Stack
  3. The Tool Factory Pattern
  4. i18n Without next-intl
  5. SEO That Scales
  6. Monetization (the honest version)
  7. What I Got Wrong
  8. Current Stats (week 2)
  9. What's Next

Why a Guy with No Free Time Started a Side Project {#why}

My daily schedule looks like this:

  • 5:00 AM — Wake up
  • 5:00–7:00 — Commute (2 hours one way)
  • 7:00–4:00 PM — Work
  • 4:00–6:00 — Commute back
  • 6:00–9:00 — Dinner, bath time, bedtime for two daughters (ages 6 and 7)
  • 9:00–11:00 — The only window I have

I've been a frontend developer for 10 years. I'm not exceptional. I'll never build the next React or invent a new state management library. But I'm fast at copying patterns that work. That's my only real skill.

One day on the train I was searching "퇴직금 계산기" (Korean severance pay calculator) and landed on a terrible website. Slow, full of pop-ups, wrong calculations. I thought: I could build this in a day. And if I build 50 of these...

Two weeks later, rhand.app has 68 free tools in 6 languages.


The Stack

Nothing fancy. I optimized for speed-to-ship, not elegance.

  • Next.js 16 (App Router)
  • Tailwind CSS 4
  • Vercel (free tier)
  • Backend: None
  • Database: None
  • Testing: Vitest + Playwright

Why no backend? Every tool runs in the browser. A loan calculator doesn't need an API. A password generator doesn't need a database. Client-side means:

  • Zero server costs at any scale
  • Zero privacy concerns (no data leaves the browser)
  • Zero DevOps

My total monthly cost is $0.


The Tool Factory Pattern

This is the only clever thing I did. Instead of building 68 unique apps, I built a factory.

Every tool follows the same structure:

src/lib/tools/{slug}/
  └── calculator.ts          # Pure functions. No React.

src/app/[locale]/tools/{slug}/
  ├── page.tsx                # Server component (SEO, metadata)
  └── {Name}Client.tsx        # Client component (interactive UI)

src/lib/dictionaries/tools/{locale}/
  └── {slug}.json             # All text: UI labels, FAQ, meta tags
Enter fullscreen mode Exit fullscreen mode

The logic layer is boring on purpose

// src/lib/tools/compound-interest-calculator/calculator.ts
export function calculateCompoundInterest(input: CompoundInput): CompoundResult {
  const { principal, monthlyContribution, annualRate, years, frequency } = input;
  const r = annualRate / 100;
  const n = frequency;
  const t = years;
  const amount = principal * Math.pow(1 + r/n, n*t)
    + monthlyContribution * ((Math.pow(1 + r/n, n*t) - 1) / (r/n));
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Pure functions. No React imports. No side effects. Testable in milliseconds.

The page layer is copy-paste

// Every page.tsx looks almost identical
export default async function ToolPage({ params }: Props) {
  const { locale } = await params;
  const dict = await getToolDictionary(locale, SLUG);

  return (
    <>
      <SEOHead ... />
      <ToolLayout ...>
        <ToolClient dict={dict} />
      </ToolLayout>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

By tool #10 I stopped thinking about architecture. By tool #40 it was muscle memory. Adding a new tool takes about 4-6 hours including translations and tests.

The boring repetition is the point. I don't need creativity. I need volume.


i18n Without next-intl

I support 6 languages: English, Korean, Japanese, Chinese, Spanish, German.

I didn't use next-intl. Instead, each tool has its own dictionary:

// src/lib/dictionaries/tools/en/tip-calculator.json
{
  "meta": {
    "title": "Tip Calculator — Split Bills & Calculate Gratuity",
    "description": "Calculate tips for restaurants, taxis, and services...",
    "keywords": "tip calculator, gratuity calculator, bill splitter..."
  },
  "heading": "Tip Calculator",
  "inputs": {
    "billAmount": "Bill Amount",
    "tipPercent": "Tip Percentage",
    "splitCount": "Split Between"
  },
  "faq": [
    { "q": "How much should I tip?", "a": "..." }
  ]
}
Enter fullscreen mode Exit fullscreen mode

68 tools × 6 languages = 408 JSON files. Sounds insane. But:

  • Each file is self-contained (no cross-references)
  • Adding a language = copying a folder and translating
  • SEO keywords are per-tool, per-language (critical for ranking)
  • No runtime overhead — loaded server-side, passed as props

SEO That Scales

Most tool sites ignore SEO. I made it a first-class concern.

Schema.org on every page

// Automatically generated for each tool
{
  "@type": "WebApplication",
  "name": "Tip Calculator",
  "applicationCategory": "FinanceApplication",
  "offers": { "price": "0" },
  "inLanguage": ["en", "ko", "ja", "zh", "es", "de"]
}
Enter fullscreen mode Exit fullscreen mode

FAQPage schema from dictionary

Every tool has 8-10 FAQ items. These serve double duty:

  1. Users actually read them
  2. Google uses them for "People also ask" rich results
// Auto-generated from the faq array in each dictionary
{
  "@type": "FAQPage",
  "mainEntity": faq.map(item => ({
    "@type": "Question",
    "name": item.q,
    "acceptedAnswer": { "@type": "Answer", "text": item.a }
  }))
}
Enter fullscreen mode Exit fullscreen mode

Long-tail keywords > short keywords

I don't try to rank for "calculator." I try to rank for:

  • "Korean severance pay calculator 2026 tax rate"
  • "tip calculator split bill 4 people"
  • "compound interest calculator with monthly contribution"

The FAQ naturally covers these long-tail variations. Each FAQ item is essentially a landing keyword.

hreflang = 6x visibility

alternates: {
  languages: {
    en: 'https://rhand.app/en/tools/tip-calculator',
    ko: 'https://rhand.app/ko/tools/tip-calculator',
    ja: 'https://rhand.app/ja/tools/tip-calculator',
    // ...
  }
}
Enter fullscreen mode Exit fullscreen mode

Instead of one page competing for "tip calculator," I have six pages competing in six different language markets. No cannibalization.

Internal linking with semantic clusters

I don't just link to "tools in the same category." I built a relationship map:

const TOOL_RELATIONSHIPS = {
  'loan-calculator': ['compound-interest-calculator', 'rent-vs-buy-calculator', 'take-home-pay'],
  'tip-calculator': ['currency-converter', 'tax-free-calculator', 'percentage-calculator'],
  // 68 tools mapped
};
Enter fullscreen mode Exit fullscreen mode

Each tool links to 3-4 semantically related tools, not just category neighbors. "Loan calculator" links to "compound interest" and "rent vs buy" — because that's the user's actual journey.


Monetization (the honest version)

I'm making $0 right now.

AdSense is pending review. I built the ad slots into every tool page, but they're empty. The plan:

  • Primary: AdSense display ads (2-3 slots per tool page)
  • Secondary: Affiliate links where natural (shoe retailers on shoe size converter, etc.)
  • Target: $10K MRR (ambitious, I know)

The math I'm betting on:

68 tools × ~5,000 monthly visits each = 340,000 pageviews
340,000 × $3 RPM = ~$1,000/month

Scale to 200 tools = ~$3,000/month
Improve RPM with targeted content = $5,000-10,000/month
Enter fullscreen mode Exit fullscreen mode

This is all projection. Reality could be 10x worse or 10x better. I'll share real numbers when I have them.


What I Got Wrong

1. I built too many tools before validating demand

I should have built 10 tools, gotten traffic, then built 10 more. Instead I built 68 in a sprint. Some of them probably have zero search demand. I won't know which ones until Search Console data matures.

2. I underestimated content quality

Tools are easy. Writing good FAQ content in 6 languages is hard. Some of my Japanese and Chinese descriptions are mediocre. I'll need to revisit these.

3. I ignored community from the start

Building in isolation means no early feedback. I should have posted on dev.to, Reddit, and Twitter from day one. I'm starting now — two weeks late.

4. AdSense approval takes time

I assumed "build it and ads will come." AdSense review is still pending after two weeks. No revenue until that's resolved.


Current Stats (Week 2)

Let me be completely honest:

  • 68 tools live
  • 6 languages (en, ko, ja, zh, es, de)
  • 237 pages indexed by Google
  • 263 keywords showing impressions in Search Console
  • ~25 visitors/week (yes, that's tiny)
  • $0 revenue (AdSense pending)
  • $0 costs (Vercel free tier)
  • 2 weeks of development

Top Search Console keywords (all 0 clicks, but showing impressions):

  • "utm builder" — 38 impressions
  • "utm generator" — 31 impressions
  • "xml sitemap" — 28 impressions
  • "favicon checker" — 19 impressions

It's early. Very early. But 237 pages are indexed and 263 keywords are showing up. The SEO foundation is there. Now it's a waiting game.


What's Next

This month

  • Get AdSense approved
  • Post on dev.to, Product Hunt, and relevant communities
  • Monitor Search Console — double down on tools that get impressions

Next 3 months

  • Add 30 more tools based on actual search demand (not guessing)
  • Improve content quality for JA/ZH translations
  • Build backlinks through content marketing

6 months

  • Target: 100+ tools, 50K monthly visits, first $1K month
  • Start sharing revenue numbers publicly

The Part Nobody Tells You

I've been a developer for 10 years. I've seen dozens of "I quit my job and built a SaaS" success stories. They make it look like the hard part is building. It's not.

Building is the easy part. I proved that — 68 tools in 2 weeks. The architecture works. The code is clean. The SEO is set up properly. The translations are done.

And yet I have 25 visitors a week.

Nobody is coming.

I think a lot of developers fall into this trap. We're comfortable building. We can always add one more feature, fix one more bug, refactor one more component. It feels productive. But it's actually procrastination disguised as work.

The real challenge — the one I'm facing right now — is everything that happens after you ship:

  • Writing about your project (like this post)
  • Sharing it in communities without feeling like a spammer
  • Waiting months for SEO to kick in
  • Accepting that most of your 68 tools might never get traffic
  • Doing marketing when your entire career has been engineering

I have a 4-hour daily commute and two young daughters. I get about 2 hours a night to work on this. Some nights I fall asleep before I even open my laptop. The dream of "passive income" and "indie developer freedom" sounds romantic until you're on month 1 with $0 revenue and 25 weekly visitors.

But here's why I'm not stopping:

My commute costs me 4 hours every day. That's 1,000 hours a year of my life spent on trains. If this project — or the next one, or the one after that — eventually generates even $2,000/month, I can work remotely. I get those 1,000 hours back. I get to see my kids in the morning.

That's not a business plan. That's a reason to keep going.


Try It

rhand.app — 68 free tools, 6 languages, no signup.

I'm not going to pretend this is a polished product. I built 68 tools in 2 weeks. There are bugs. Some translations are awkward. Some calculators might have edge cases I haven't found.

But every tool works. Every tool is free. Every tool runs in your browser without sending your data anywhere.

If you've tried to build passive income as a developer and struggled — I'd genuinely love to hear your story. Not the success story. The honest one. What did you try? Where did you get stuck? Are you still going?

Drop a comment below — I read every one.

Top comments (0)