Next.jsReactE-commerceStarter

nextjs-commerce — Next.js Ecommerce Template

By rekey-dev

A digital product storefront template built on Next.js and SQLite, with the product catalog stored in SQLite, accounts and payments handled by Rekey, and ownership recorded as an entitlement after purchase.

Explore the design

Compare the page layouts and mobile experience. Open any screenshot for a closer look.

3 previews
Desktop homepage1 / 3
Desktop captureTop of page

Actual screenshot captured by the server browser.

All pages & views 3

Screenshots show visual appearance; interactive behavior may differ.

Recorded 2026-10-02View source

What stands out

A quick read of the recorded project description.

01

Product catalog stored in SQLite, written from data/products.json via npm run seed

02

Accounts and payments connect to Rekey; ownership is recorded as an entitlement rather than an order table

03

lib/db.ts opens the database read-only, so the application cannot write ownership facts

04

The download route verifies the entitlement itself and restricts paths to within downloads/

05

When the entitlements call fails, it returns an empty set, denying downloads by default

06

Only one product is settled at a time; there is no shopping cart

Template Details

This is a runnable digital product storefront template: the product catalog lives in a SQLite file, accounts and payments connect to Rekey, and what a buyer purchases is recorded as an entitlement rather than through an order table that must be kept in sync manually. The repository notes explicitly that it sells one digital product at a time and has no shopping cart; this is a limitation imposed by the billing model rather than an omission—Rekey checkout settles per plan, so putting four products in a cart would mean four separate checkouts. If true multi-item single-payment checkout is needed, the template author advises against using it as a starting point and points out that app/actions/buy.ts is the file that would need to change.

The catalog and ownership records are intentionally split into two systems: SQLite stores what the product is (name, copy, price, corresponding file), while Rekey stores who paid and answers “does this person own it” via an entitlement. To avoid two contradictory answers after refunds, chargebacks, or expired cards, lib/db.ts opens the database read-only, so the application cannot write ownership facts; only npm run seed writes them.

The download route verifies the entitlement itself rather than trusting the page that rendered the link; it also restricts paths to the downloads/ directory and rejects out-of-bounds paths. When the entitlements call fails, it returns an empty set, meaning the default on failure is that no one can download. The repository also notes that real files are better placed behind object storage with signed URLs; the verification logic stays the same, and only the last few lines of the route need to change.

Next.jsE-commerceStarter

Is it right for your project?

Use Cases

  • Selling digital goods such as downloadable files, fonts, presets, ebooks, or licenses
  • Needing a Next.js starter project that separates the product catalog from payment/ownership
  • Wanting a storefront that uses Rekey for accounts, checkout, and entitlement verification
  • Serving as a starting point for later migrating the catalog to a CMS and files to object storage

Suitable For

  • Individuals or small teams selling a single digital product
  • Projects that accept a billing model with no shopping cart and per-plan, one-at-a-time checkout
  • Developers willing to register or self-host a Rekey Application
  • Developers using Next.js who want the catalog and ownership records separated

Getting started & customization

Use these recorded setup notes alongside the repository instructions for your chosen version.

  1. 1

    A Rekey Application is required: register at rekey.dev, or deploy Rekey yourself from the open-source repository

  2. 2

    After cloning the repository, run npm install, copy .env.example to .env.local, and fill in Panel → Developer → API keys

  3. 3

    Run npm run seed to generate shop.db, then create the corresponding slug, price, and feature entitlement in Panel → Billing → Plans as output, and connect a payment provider in Billing → Providers

  4. 4

    Environment variables include REKEY_SECRET (server-side only), REKEY_URL, NEXT_PUBLIC_REKEY_PUBLIC_KEY, NEXT_PUBLIC_REKEY_URL, NEXT_PUBLIC_APP_URL, and DATABASE_FILE

  5. 5

    When deploying, ensure the product catalog file exists (run seed at build time or point DATABASE_FILE to a mounted volume), and set NEXT_PUBLIC_APP_URL to the real origin and add it to the panel's allowed origins

Read repository instructions

License & source material

Usage conditions & limitations

Free to use · keep license notice

This entry covers the free version under the MIT license in the public repository; the free scope is that version's code and does not mean external services or third-party assets are free.

Must comply with when using

  • You may use, modify, commercialize, and distribute this version's code for free.
  • When distributing copies or substantial portions of the code, retain the original author's copyright notice and the full MIT license notice.
  • The code is provided as is; warranties and limitations of liability are governed by the original license and applicable law.

Additional costs & items to verify

  • Using this template requires a Rekey Application: register at rekey.dev, or deploy Rekey yourself from the open-source repository. Accounts, checkout, and entitlement verification all depend on this external service and cannot run independently of Rekey.
  • Each plan (slug, price, and feature entitlement) must be created manually in the Rekey panel according to the seed output, and a payment provider must be connected in Billing → Providers; otherwise the purchase flow cannot be completed.
  • Environment variables such as REKEY_SECRET (including server-side-only secrets) must be configured, and API keys must be obtained yourself; leaking a key would allow abuse of full API access for that Application.
  • The template settles only one digital product at a time and has no shopping cart; Rekey checkout settles per plan, so multiple products require multiple checkouts. Projects that need multiple products in a single payment are not suitable.
  • For downloading real files in production, it is recommended to use object storage with signed URLs rather than streaming through the application server, which may incur additional object storage service costs.
  • When deploying, the product catalog file must exist: run npm run seed at build time, or point DATABASE_FILE to a writable mounted volume; otherwise the application cannot read the catalog.
  • Demo images, fonts, icons, and other third-party assets must be checked against their respective licenses and cannot be judged solely by the code's MIT license.
  • External services such as domains, hosting, databases, payments, or AI may incur separate charges.
View Original Text Basis

Permission is hereby granted, free of charge

Original License Text

You need a Rekey Application. Sign up at [rekey.dev](https://rekey.dev), or run it yourself from [the open source repo](https://github.com/rekey-dev/rekey).

Original README

Create each one in **Panel → Billing → Plans** with that slug and price, and give it a feature entitlement with that key. Connect a payment provider under Billing → Providers.

Original README

`REKEY_SECRET` | Server-only. Full API access for this Application. Never commit it.

Original README

If you need a real multi-item basket with one payment, this template is the wrong starting point today.

Original README

For real files, put them behind signed object-storage URLs rather than streaming from the app server.

Original README

The catalogue is a file, so it needs to exist wherever the app runs: run `npm run seed` as part of the build, or point `DATABASE_FILE` at a mounted volume.

Original README
https://github.com/rekey-dev/nextjs-commerce/blob/main/LICENSEhttps://github.com/rekey-dev/nextjs-commerce/blob/main/README.md

Compiled by DeepSeek based on repository license and README ·2026-10-02. Results only cover the materials read; dependencies, images, fonts, and subsequent term changes are subject to their respective original texts.

Project checks & description sources

Template Information

Source platforms
GitHub
Documentation and maintenance reference score
More documented · 85 / 100
Technologies
Next.js · React
Website type
E-commerce · Starter
How this score is calculated

Our heuristic uses license records (up to 30), a demo link (12), a preview (10), stars (15), recorded maintenance (15), description completeness (8), feature records (5) and classification data (5). It is not a code, security or performance test, or a Google score. A link does not establish tested functionality.

What to verify for this project

Repository update recorded
2026-08-14
Screenshot last checked
2026-10-02

A demo link is listed. Check it with your own content and viewport sizes; its presence does not confirm that forms, authentication or payments work.

The catalog identifies Next.js as the primary stack. Check the actual dependency manifest and lockfile for versions and hosting requirements. These notes use recorded metadata, not a code audit or a deployment test.

Use a selection guide →

How this description was prepared

AI-assisted description based on catalog records and repository material. Repository authors are not automatically the reviewers of this description.

Automatically checked against source and license rules; no manual editorial review.

Inspect the recorded source material

SHA-256: f06cbdc941ab441548b3041e196f6dc1744981df70c245c1861e2f18e05910df

2026-10-02T07:36:38.479Z

Source file / repository
# Shelf: a digital shop on Next.js, SQLite and Rekey

A working storefront. The catalogue is a SQLite file, accounts and payments are
[Rekey](https://rekey.dev), and what a buyer owns is an entitlement rather than
a row somebody has to remember to update.

- Next.js 16 (App Router, Turbopack, React 19)
- SQLite via better-sqlite3, seeded from a JSON file
- Tailwind CSS 4
- `@rekey.dev/nextjs`, `@rekey.dev/react`, `@rekey.dev/node`

## What this is, and what it is not

It sells **digital goods, one at a time**. There is no basket, and that is a
property of the billing model rather than a corner cut: Rekey checkout sells a
plan, so four things in a basket would be four checkouts. For downloads, fonts,
presets, books and licences that is usually the right shape anyway.

If you need a real multi-item basket with one payment, this template is the
wrong starting point today. `app/actions/buy.ts` is the file that would have to
change, and [the tracking issue is here](https://github.com/rekey-dev/rekey/issues/16).

## Getting it running

You need a Rekey Application. Sign up at [rekey.dev](https://rekey.dev), or run
it yourself from [the open source repo](https://github.com/rekey-dev/rekey).

```bash
git clone https://github.com/rekey-dev/nextjs-commerce my-shop
cd my-shop
npm install
cp .env.example .env.local     # fill in from Panel -> Developer -> API keys
npm run seed
npm run dev
```

`npm run seed` creates `shop.db` from `data/products.json` and then prints the
plans you need, like this:

```
grid-icons             49.00 USD  grants  product_grid_icons
field-notes-preset     29.00 USD  grants  product_field_notes
```

Create each one in **Panel → Billing → Plans** with that slug and price, and
give it a feature entitlement with that key. Connect a payment provider under
Billing → Providers. That is the whole setup.

### Environment

| Variable | What it is |
| --- | --- |
| `REKEY_SECRET` | Server-only. Full API access for this Application. Never commit it. |
| `REKEY_URL` | `https://api.rekey.dev`, or your own API if you self-host. |
| `NEXT_PUBLIC_REKEY_PUBLIC_KEY` | Safe in the browser. Identifies the Application. |
| `NEXT_PUBLIC_REKEY_URL` | Same API, the browser-visible copy. |
| `NEXT_PUBLIC_APP_URL` | Where the shop is reachable. Checkout returns the buyer here. |
| `DATABASE_FILE` | Where `shop.db` lives. Anywhere writable. |

## The one idea worth taking

**The catalogue and the ownership record are separate systems, on purpose.**

SQLite holds what a product *is*: name, copy, price, which file it ships. Rekey
holds who *paid*, and answers "does this person own it" as an entitlement.

The temptation is an `orders` table, because it is five minutes of work and it
feels like the shop owns its own data. Then a refund happens, or a chargeback,
or a card expires, and there are two answers to the same question with no rule
about which wins. The shop keeps serving files to somebody who got their money
back, and nothing anywhere looks broken.

So `lib/db.ts` opens the database **read-only**. The app physically cannot write
an ownership fact. `npm run seed` is the only writer.

## What is where

| File | What it does |
| --- | --- |
| `data/products.json` | The catalogue, in the form you edit. |
| `scripts/seed.mts` | Loads it into SQLite. The only thing that writes. |
| `lib/db.ts` | Read-only queries. Opens lazily, which matters at build time. |
| `lib/entitlements.ts` | What the signed-in buyer owns. Fails closed. |
| `app/actions/buy.ts` | One product, one checkout. |
| `app/api/download/[slug]/route.ts` | The only route that hands over a file. |
| `app/library/page.tsx` | What they bought, with download links. |
| `app/actions/auth.ts` | Sign up, sign in, sign out. |
| `proxy.ts` | A cookie-presence gate at the edge. The pages still do the real check. |

## Buying

```ts
const { url } = await rekey().billing.createCheckout(session.accessToken, {
  planSlug: product.planSlug,
  successUrl: `${appUrl}/library?bought=${slug}`,
  cancelUrl: `${appUrl}/p/${slug}?checkout=canceled`,
});
redirect(url);
```

The subscription is `PENDING` until the provider webhook confirms payment, which
is why `/library` says "usually seconds" instead of showing an empty shelf as if
the purchase failed. Rekey handles that webhook; you do not need an endpoint.

## Downloads

The download route checks the entitlement itself rather than trusting the page
that rendered the link, because a link is a string and anyone can type one. It
also resolves the path inside `downloads/` and refuses anything that escapes,
so a bad row in the catalogue cannot become an arbitrary file read.

`lib/entitlements.ts` returns an empty set if the entitlements call fails. A
billing outage should mean nobody can download for a minute, not that everybody
can.

For real files, put them behind signed object-storage URLs rather than streaming
from the app server. The check stays exactly the same; only the last three lines
of the route change.

## Growing out of SQLite

A file is the right call for a starter and the wrong call once somebody who is
not you needs to edit product copy. When that happens, move the catalogue into
a CMS and leave everything else alone: only `lib/db.ts` knows where products
come from, and it is thirty lines.

[Payload](https://payloadcms.com) is the natural fit, and a Rekey plugin for it
is [tracked here](https://github.com/rekey-dev/rekey/issues/13). Until then,
Payload's local API drops into `allProducts()` and `productBySlug()` directly.

## Deploying

The catalogue is a file, so it needs to exist wherever the app runs: run
`npm run seed` as part of the build, or point `DATABASE_FILE` at a mounted
volume. `downloads/` is the same story, and is the first thing you should move
to object storage.

Set `NEXT_PUBLIC_APP_URL` to the real origin and add it to the Application's
allowed origins in the panel.

## Licence

MIT. Take it apart.
Report a correction

Explore similar templates

Compare previews and descriptions within the same stack and category.