Add an ecommerce store to a Next.js app with Supabase

This guide turns an existing Next.js (App Router) project into a store with Stripe checkout and an admin dashboard. It takes about ten minutes. At the end you have:

Everything lives in your repo and your Supabase project: the schema, the API (Supabase Edge Functions) and the storefront components. There is no platform fee and no third-party backend. Sellbase is open source (MIT).

In Spanish: Agrega una tienda en línea a tu app de Next.js con Supabase.

Why not build it by hand?

The usual recipe is a products table in Supabase, a Server Action that creates a Stripe Checkout Session and a webhook route that writes the order. It works for a first sale. Then the store also needs:

Sellbase is that whole layer, already built and tested:

Requirements

1. Install

From the root of your project:

npx supabase init            # skip it if you already have supabase/config.toml
npx supabase start
npx sellbase init --yes

sellbase init does the following:

Want sample products to see it working? Run npx sellbase seed cafeteria. The other presets are ropa, curso and consultorio.

For a hosted Supabase project, pass its keys instead of starting the local stack:

npx sellbase init --yes --supabase-url https://<project>.supabase.co --anon-key … --service-role-key … --db-url … --owner-email you@example.com

The service role key is used only during init. It is never written to your frontend.

2. Wire the provider and the theme

Wrap the app once in app/layout.tsx:

import { CartButton, CartDrawer } from '@/components/sellbase/cart-drawer';
import { SellbaseStoreProvider } from '@/components/sellbase/provider';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <SellbaseStoreProvider>
          <header>
            <a href="/">My store</a>
            <CartButton />
          </header>
          <main>{children}</main>
          <CartDrawer cartHref="/cart" />
        </SellbaseStoreProvider>
      </body>
    </html>
  );
}

The admin brings its own full-page layout. If your header shouldn't show on /admin, put the store pages in a route group such as app/(store)/layout.tsx, as the example store does.

Import the theme in your global CSS and set your brand. With Tailwind v4:

@import 'tailwindcss';
@import '../components/sellbase/theme.css';
@source '../components/sellbase';

:root {
  --sb-primary: #3f2a1d; /* buttons and accents */
  --sb-radius: 14px;
}

3. Add the store pages

// app/page.tsx: catalog
import { ProductGrid } from '@/components/sellbase/product-grid';

export default function Home() {
  return <ProductGrid />;
}
// app/products/[slug]/page.tsx: product page with SEO
import { createSellbase, productMetadata } from '@sellbase/react';
import { ProductDetail } from '@/components/sellbase/product-detail';
import { ProductJsonLd } from '@/components/sellbase/product-seo';

const sellbase = createSellbase({
  url: process.env.NEXT_PUBLIC_SELLBASE_URL!,
  anonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
});
type Props = { params: Promise<{ slug: string }> };

export async function generateMetadata({ params }: Props) {
  const product = await sellbase.products.get((await params).slug).catch(() => null);
  return product
    ? productMetadata(product, { url: `https://example.com/products/${product.slug}` })
    : {};
}

export default async function ProductPage({ params }: Props) {
  const { slug } = await params;
  const product = await sellbase.products.get(slug).catch(() => null);
  return (
    <>
      {product && <ProductJsonLd product={product} url={`https://example.com/products/${slug}`} />}
      <ProductDetail slug={slug} />
    </>
  );
}
// app/checkout/page.tsx
import { Checkout } from '@/components/sellbase/checkout';
export default function CheckoutPage() {
  return <Checkout successPath="/thanks" cancelPath="/cart" />;
}

// app/thanks/page.tsx: waits for the webhook, shows the order and clears the cart
import { CheckoutReturn } from '@/components/sellbase/order-status';
export default function ThanksPage() {
  return <CheckoutReturn />;
}

There is also <CartPage /> (cart-page), <OrderLookup /> for a "my order" page and <ProductCarousel />. The components read everything from the API, so products and prices are never hardcoded.

4. Add products and connect Stripe

Run npm run dev, open http://localhost:3000/admin and sign in with the credentials in .env.sellbase:

  1. Products → Add product: add photos, a price, variants and stock.

  2. Settings → Payments: paste your Stripe test secret key (sk_test_…). It is stored in Supabase Vault.

    • On a deployed project, the Stripe webhook is created for you.

    • Locally, forward it with the Stripe CLI and paste the whsec_… it prints:

      stripe listen --forward-to http://127.0.0.1:54321/functions/v1/sellbase-webhooks/stripe
      
  3. Test purchase: buy something with card 4242 4242 4242 4242. The order appears in Orders once the webhook confirms the payment.

npx sellbase doctor lists anything still pending, with the next step for each.

Or let your agent do it

init registered the Sellbase MCP server in .mcp.json. Open Claude Code or Cursor in the project and say:

Set up my store with Sellbase: I sell handmade candles, $250 MXN each, shipping $120.

The agent creates the products, connects Stripe in test mode and runs a test purchase until it succeeds. Money and destructive actions always ask you first and are written to the audit log.

5. Deploy

Full guides: deploy and live payments with Stripe.

Start from a template instead

npx sellbase create my-store --template nextjs
cd my-store && npm install && npm run setup && npm run dev

It is a complete Next.js 16 + Tailwind store with a Vercel deploy button: examples/nextjs-store. Using plain HTML, WordPress or Webflow instead of React? Use --template html, which uses the <sellbase-*> web components.

Raw Markdown for agents: guides/nextjs-supabase-ecommerce.md · Edit on GitHub