DommoLabs.
Back to work
Product Consumer 2025

Littelist

A family gift-giving coordinator where Grandma doesn't need an account — one source of truth for who's buying what, and no more duplicate Lego sets at Christmas.

  • StatusLive at littelist.com
  • ScopeSolo, end-to-end product
  • TimelineYears brewing · launched Dec 2025
  • UseFree · gifters need no account
Case study preview Littelist

Full product walkthrough coming soon.

The problem

Birthdays and Christmas turn into a scramble across a dozen group chats. Did anyone get the Lego set? I already bought that. What size does he wear now?

The result is duplicate gifts, wasted money, stuff the kid didn't want, and a surprising amount of landfill. One parent ends up as the bottleneck — fielding questions at 10pm, reconciling purchases in their head, and quietly matching a wishlist to a dozen well-meaning relatives. The app that fixes this has to work for both sides: a parent who's happy to log in, and a grandparent who is not installing anything, not remembering a password, and not creating an Amazon account on your behalf.

What I built

One wishlist, two audiences, and a claim system that holds up in the Christmas rush.

Littelist is a two-sided family gift-giving coordinator. Parents get an authenticated dashboard — multiple children with avatars and birthdays, multi-occasion lists, four item types (a specific product, an open theme like “art supplies,” a group-funded bigger gift, an ongoing pot like summer camp), a structured gifting guide, and a coordination view that shows live claim state. Gifters get a magic-link session with no account and no password — a filterable list, one-tap claim/unclaim, multi-claim themes for group-gift coordination, and a personal dashboard across every family they're buying for. Daily reminders fire in plain English: “3 items still unclaimed, 7 days to go.”

Built with
  • Next.js 16
  • Supabase (RLS · RPC)
  • React 19 · Server Actions
  • Upstash Redis
  • SendGrid + React Email
  • Vercel Cron
Product preview Littelist

Preview panels will live here once the full case study is authored.

How it works

Three decisions shaped the product more than anything else.

1

The data model comes from how gift-giving actually breaks.

Most wishlist apps model a list as rows of products. That covers maybe a third of how families actually gift. So Littelist has four item types: *specific* (a named product, one claim, done), *theme* (an open category like “art supplies” — multiple gifters, different items), *shared_gift* (group funding toward one bigger thing), and *fund* (an ongoing pot, like summer camp or a 529). Each type has its own claim semantics, encoded in the schema, not the UI.

A second pattern came from a smaller failure mode: a gifter clicks out to Amazon, gets distracted, never comes back. Now the item sits claimed for days, blocking other gifters. So localStorage records the intent on click, and when the tab becomes visible again, the app prompts — *still getting this?* — before letting the hold persist. Small feature. Catches the failure mode that kills coordination apps.

Workflow preview The data model comes from how gift-giving actually <em>breaks</em>.
2

One identity, two auth paths.

The hardest modelling problem was that the same person might arrive twice. A grandmother clicks a magic link in December and gets a 30-day session to buy a nephew's Lego set. In March, her own kid invites her as a gifter to a birthday list. She's still the same gifter. In August she might sign up as a parent herself, managing her own child's wishlist. Same person, three auth states, one identity.

Rather than branch the codebase on “auth user vs. session user,” every gifter — authenticated or not — gets a row in a single gifters table. A Postgres function, get_participant(), checks auth.uid() first and falls back to the session token, returning one unified Participant type. Email changes sync between auth.users and gifters. A permission matrix collapses into a single identity lookup.

Annotated workflow One identity, two auth <em>paths</em>.
  1. 1
    Race-safe claims in Postgres, not the app. Two aunts hit “claim” on the last Lego set in the same second; only one should win. create_claim_safe() is a SECURITY DEFINER function — it checks is_closed, inspects theme_mode, and inserts in a single transaction. Correctness at the database boundary, where it's guaranteed.
  2. 2
    RLS is the permission model. Parents see only their own recipients; gifters see only lists they've been invited to — enforced by Postgres policies, not application middleware. A bug in a route handler can't leak data. The database refuses.
  3. 3
    Reminders deduped at the DB, not the sender. A sent_reminders table keyed by (gifter, list, days_before) means a cron retry can't double-send. The product's value proposition dies if reminders arrive twice or not at all — so the guarantee lives in the schema.
3

Push the hard guarantees down the stack.

The pattern keeps showing up. Authorisation in RLS. Transactional claim logic in Postgres functions. Rate limits in Upstash Redis so a bad actor can't exhaust one Vercel region. Email retries with exponential backoff, with retryable vs. permanent errors distinguished at the transport layer. The application code — Next.js route handlers, React server components — is thinner and safer because the hard guarantees live closer to the data.

The payoff is a small client bundle, no client state library, and a clear answer to “where does this data come from?” — one line per page. Server components read Supabase directly; mutations are server actions; anything transactional is a Postgres function. Redux, Zustand, tRPC: none of it turned out to be necessary.

Workflow preview Push the hard guarantees <em>down the stack</em>.
Where it is now

Live at littelist.com since December 2025. Two auth models unified on one identity table. Claims, reminders, and rate limits all enforced at the data layer. The concept had been brewing for years — this is the version that finally shipped.

Status Live · littelist.com
Use Free · no account for gifters
Cadence Shipped Dec 2025 · active polish
§ Stack and specifics for the builders in the room
Role Concept, product, UX, architecture, build. Everything — the idea lived in my head for years before the refined version launched in December 2025.
Timeline Years brewing · launched Dec 2025 · actively polished through 2026
Stack
Next.js 16 App Router · React 19 · Server ActionsTypeScript · Supabase Postgres · RLS · SECURITY DEFINER RPCsMagic-link gifter sessions · 30-day httpOnly cookiesSendGrid + React Email · retry with exponential backoffUpstash Redis · distributed rate limiting · in-memory dev fallbackVercel · daily cron · serverless functionsshadcn/ui · Radix · Tailwind · bcrypt-hashed coordination PINsCloudinary · Umami + Vercel Analytics
Access Public · free · gifters need no account
Why this stack Gift coordination is intrinsically relational — recipients, lists, items, claims, gifters, with junction tables for multi-recipient items — so Postgres with RLS does the job an entire middleware layer would otherwise do. RPCs give transactional claim logic at the data boundary, which is the only place race conditions are guaranteed handled. Magic-link sessions remove 100% of password-related drop-off for gifters; a minute of verification friction once a month is worth it to let a grandparent buy a gift without installing anything. Upstash Redis because in-memory rate limits don't survive across serverless regions. React Email because PR-reviewable templates beat maintaining a second templating system. And no i18n, no client state library, no payment layer for a free-forever product — the codebase solves the problem in front of it.