Originally written November 2025 — rewritten and continuously updated since.

On Building the Cherrypick Website

I run a Korean-language newsletter about travel deals — mileage promotions, credit card offers, hotel and airline sales. It lives on Substack, and Substack is great at exactly one thing: sending emails. Everything else about it fought me.

The core problem

each newsletter issue bundles several deals into a single post. That's fine in an inbox, but terrible as an archive. A reader who remembered "that Marriott promo from a few weeks ago" had no way to find it — no per-deal pages, no search, no filtering. Deals that were still valid got buried under newer issues. And Substack's SEO is weak for Korean content — no proper language support, thin meta tags — so Korean readers couldn't even discover the content through search.

So I built cherrypick.penielcho.com: a place where every deal lives as its own searchable, indexable page, with its own URL, its own expiry date, and its own metadata.

The stack (and why it's deliberately boring)

  • Nuxt 4 + Vue 3 — SSR out of the box, which is the whole ballgame for SEO. I wanted to stay in the Vue ecosystem, and Nuxt's file-based routing plus Nitro server routes meant I didn't need a separate backend.
  • Tailwind CSS (+ typography plugin) — fast styling, and prose handles long-form deal content without me hand-tuning every heading.
  • Supabase — Postgres, auth, and storage in one place. Deals, notices, editor profiles all live here, with RLS on every table and the service-role key strictly server-side behind Nitro API routes.
  • Vercel — hosting, analytics, and Speed Insights. Route-level cache rules map cleanly to Vercel's edge.
  • No CMS — markdown is enough. I built a small admin area (/admin) with a markdown editor instead of dragging in a headless CMS I'd have to maintain.

The guiding principle: this is a one-person project, so every dependency is a liability. If Nuxt or Supabase already does it, I don't add a tool for it.


The launch version (December 2025)

The first public version shipped with the ideas that motivated the whole thing:

  • A landing page split into "fresh" and "expiring soon" — so urgent deals are visible immediately instead of buried chronologically.
  • Server-side filters for brand, category, and validity dates — server-side so they're fast and the filtered views are crawlable.
  • Deadline tags on deal cards ("D-3", "expired") so scanability doesn't depend on reading each card.
  • Dark mode with a system-preference-aware toggle — took more iterations than I'd like to admit to get tag contrast right in both themes.
  • Pagination + skeleton loading so the deal list stays fast as the archive grows.
  • Editor credits and a notice section — the newsletter has multiple contributors, so deals carry an editor profile, managed through a small CMS in the admin area.
  • Invite-code admin signup — editors onboard via database-backed invite codes, validated through a server route using Supabase's service role. No open registration, shared passwords.
  • Semantic headings, proper focus styles, real meta/OG tags, and a sitemap from day one — the unglamorous stuff that was the entire point of leaving Substack.

I also shipped a live exchange-rate ticker in this era. It was fun. It was also visual noise that had nothing to do with finding deals, so it didn't survive (see May).


May 2026 update — performance, SEO, and the Guide

After a few months of real traffic, two things were clear: mobile performance needed work, and readers wanted evergreen reference content, not just time-limited deals.

Performance. I went through Lighthouse systematically: route-level SWR caching plus useLazyAsyncData cut TTFB and LCP on public pages, and mobile scores came up meaningfully. Lesson learned: on a content site, caching strategy is the performance strategy — component-level micro-optimization barely moved the needle compared to getting cache headers right.

GEO, not just SEO. People increasingly ask ChatGPT and Claude for deal recommendations, so I added llms.txt / llms-full.txt manifests and opened crawler access for AI agents, alongside a dynamic sitemap.xml. Search engines aren't the only discovery surface anymore.

The Cherrypick Guide. New evergreen section (originally /services, renamed to /guide with a 301) for reference content — which booking services to use, how mileage programs work. Markdown-rendered sections, deep-linkable anchors, and its own admin UI with filtering and pagination.