DEV Community

Tim
Tim

Posted on

How I built a sourdough hydration calculator with Next.js 14 and Cloudflare Pages

Six months ago I got tired of doing hydration math on a napkin. So I built SourdoughCalc - a free calculator with 34 SEO-friendly articles.

Six months ago I got tired of doing hydration math on a napkin every time I baked sourdough at home. So I built SourdoughCalc - a free hydration calculator with 34 SEO-friendly articles. Here is what I learned shipping a zero-cost indie project.

The Stack

  • Next.js 14 App Router (TypeScript + Tailwind)
  • Cloudflare Pages for hosting ($0/mo, global CDN, sub-second TTFB)
  • Playwright for e2e tests
  • 34 hand-written articles targeting sourdough keywords

Total monthly cost: $0.

The SEO Strategy That Worked

I did not try to rank for "sourdough" (impossible for a new site). Instead I targeted long-tail:

Keyword Monthly Volume Position
"65% hydration sourdough" ~7,200 Top 5
"sourdough hydration calculator" ~4,500 Top 3
"high vs low hydration sourdough" ~2,000 Top 10

Every article follows the same template:

  1. Answer the question in the first 100 words
  2. Add a FAQ schema block (FAQPage JSON-LD)
  3. Link to 3-5 other articles in the cluster

What I Would Do Differently

  • GSC indexing speed: Do not batch-submit. Submit URL individually for faster pickup.
  • FAQ schema: Mandatory. Google shows FAQ rich snippets, which doubles CTR.
  • Comparison tables: I added 3-column comparison tables in 6 articles. They get featured snippets.

The Real Win

The calculator is the hook. 34 articles are the asset. SEO compounds. Six months in, I am getting 5x more organic traffic than the launch day spike ever was.

Try It

Free, no signup: sourdough-hydrationcalculator.com

If you have questions about the SEO or the build, drop a comment. I will answer.

Top comments (5)

Collapse
 
alexshev profile image
Alex Shev

I like the emphasis on a source of truth. The same approach prevents local sites from drifting when CMS copy, schema, sitemaps, and Google Business Profile details are maintained by different people.

Collapse
 
yx-studio profile image
Tim

You nailed the real pain point. The drift problem is exactly why I
ended up rewriting the sitemap twice in the first month - the source
of truth was in the TypeScript file, but the static XML had already
shipped to Cloudflare with stale URLs.

For local sites, the Google Business Profile piece is one I hadn't
thought about - do you sync it programmatically or treat it as
manual? I've seen small businesses where it's updated 4 different
times a year and the addresses/categories silently drift from the
site copy. Feels like the kind of thing where the schema.org
LocalBusiness JSON-LD should be the source and the GBP dashboard
should ingest, but Google's tools don't make that easy.

Collapse
 
yx-studio profile image
Tim

Thanks — and that local drift point is exactly what I was designing
against. The failure mode I didn't expect: schema drift is easy to
catch, but GBP (Google Business Profile) drift is the silent killer.
NAP inconsistencies across GBP, schema, and CMS footer slowly erode
local pack rankings over months.

What I started doing: a weekly cron that hashes the canonical NAP +
hours + categories from the source of truth, diffs it against GBP
via the API, and emails me if anything moves. Caught 2 silent GBP
edits by Google itself in the first month.

Curious what your source of truth looks like for local SEO specifically
— Notion, single CMS field, or a YAML in the repo?

Collapse
 
alexshev profile image
Alex Shev

I use a canonical structured record rather than a free-text doc: business identity, phone, address, hours, categories, services, and evidence fields. The CMS and schema should render from it; GBP is a separately observed surface. Your hash/diff is exactly the useful separation: it catches both accidental drift and a provider-side change without pretending the API is the source of truth. I’d also log the previous/new value and actor where possible, so the alert becomes an audit trail rather than just a warning.

Thread Thread
 
yx-studio profile image
Tim

Audit trail is the missing piece — agreed. My current diff just shows
current vs canonical, no actor or timestamp. Was about to add this
when I move from a one-off cron to a real audit log, but the harder
question is attribution: when Google itself standardizes a category
or merges two GBP entries without warning, who's the "actor" in your
audit trail?

On the evidence field — I trust whoever edits the YAML right now,
which is fine when it's just me but breaks the moment you have a VA
or marketing person touching the source. How are you capturing
evidence at scale without the record becoming a wiki?