BoilerplateHub

Next.js + Supabase + Auth.js + Lemon Squeezy

Next.js with Supabase, Auth.js and Lemon Squeezy pairs a thin auth layer with a merchant of record, leaving very little of the stack that you actually own. Supabase holds the data, Auth.js holds the session, Lemon Squeezy holds the money and the tax. It suits a one-plan or few-plan product sold to individuals.

Next.js SupabaseAuth.jsLemon Squeezy

Database

Supabase

With a reseller collecting the money, the Supabase schema splits cleanly: adapter tables on one side, an orders and entitlements pair on the other, and nothing about currency or tax anywhere in between. Store the order and subscription identifiers verbatim so a support question is answerable in one query. Resist copying prices into Postgres, since the amount actually charged, after tax and discounts, was never yours to decide.

Authentication

Auth.js

Auth.js supplies sign-in and a user ID, and that ID is the only thread connecting a hosted checkout back to an account. Pass it as checkout custom data and confirm on the webhook that it matches a row in the adapter's user table before granting anything. Since Auth.js ships no invitation or organization concept, a purchase attaches to one person, and sharing a plan across colleagues is a feature you would be writing from scratch.

Payments

Lemon Squeezy

Lemon Squeezy suits a solo Next.js product where checkout is a button and compliance is somebody else's staffing problem. There is no payment intent lifecycle to model and no tax rate table to maintain. What disappears is scriptability: variants are configured in their dashboard, so a pricing experiment means clicking rather than deploying, and genuinely unusual billing shapes are simply not reachable from here.

What to watch out for

Both halves of this stack are deliberately thin, and the gap between them is where the work hides. Auth.js will never tell you a subscription lapsed, and Lemon Squeezy has no idea what a session is, so expiry, failed renewal and downgrade each need a handler written on purpose. Skipping the expiry event is the standard route by which a cancelled customer keeps full access forever.

Boilerplates close to this stack

Matched on Next.js plus the parts of this stack our catalog tags. Each card shows which pieces actually line up, so you can see how much you would still wire yourself.

A CLAUDE.md for this stack

The rules that matter for this combination specifically, including who owns entitlement state. Adapt the commands to your repository before committing it.

CLAUDE.md
# CLAUDE.md

This project is a Next.js SaaS on Supabase, Auth.js and Lemon Squeezy.

## Stack

- Framework: Next.js
- Database: Supabase
- Auth: Auth.js
- Payments: Lemon Squeezy

## Boundaries

- Never edit a migration that has already run. Write a new one.
- Never hardcode Lemon Squeezy price or product identifiers in components. They belong in config.
- Never trust a client-supplied user id. Read the session from Auth.js on the server.
- Treat webhook handlers as idempotent. The same event will arrive twice.

## Entitlements

- Lemon Squeezy is the source of truth for what a customer paid for.
- The database mirrors that state; it never decides it.
- Any check for "can this user do X" reads the mirrored entitlement, not a live API call.

## Before you say a change is done

- The app builds.
- Tests pass.
- No secret, key or webhook signing secret appears in a committed file.

Related stacks