Cart drawer with Stripe Checkout

The stack cart drawer hands its items to a server action, the server action makes a Stripe Checkout session, and the browser goes to Stripe to pay.

Get the code · $3React · Tailwind · TypeScript
Stack Cart Drawer, preview

Use it in Next.js

app/shop/page.tsx

"use client";
import StackCartDrawer from "@/components/StackCartDrawer";
import { createCheckout } from "./actions";

export default function Shop() {
  return (
    <div className="h-dvh">
      <StackCartDrawer
        autoplayDemo={false}
        onCheckout={async (items) => {
          const url = await createCheckout(items);
          window.location.assign(url);
          return { ok: true };
        }}
      />
    </div>
  );
}

app/shop/actions.ts

"use server";
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

// product id in the cart data -> Stripe Price id (create the prices in your Stripe dashboard)
const PRICES: Record<string, string> = {
  orbit: "price_xxx",
  case: "price_yyy",
  cable: "price_zzz",
};

export async function createCheckout(items: { id: string; qty: number }[]) {
  const session = await stripe.checkout.sessions.create({
    mode: "payment",
    line_items: items.map((i) => ({ price: PRICES[i.id], quantity: i.qty })),
    success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/thanks?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/shop`,
  });
  if (!session.url) throw new Error("Stripe returned no checkout URL");
  return session.url;
}

Good to know

  • onCheckout passes the items and a total. The server action ignores the total on purpose and prices from your Stripe Price ids, so nobody can edit the amount in the browser.
  • The redirect to Stripe cuts the drawer's fold-home animation short. If you want people to see it, wait about a second before window.location.assign.
  • The cart ships with three demo products. In TypeScript the data type is the demo's exact shape, so to load your own catalogue, widen the NorthwindData type in data/northwind.ts first.
  • Stock, discounts and saving the cart between visits are yours to add.

Install

  1. Copy from the zipPut react/src/components + react/src/data into src/, then npm i stripe.
  2. Or ask your AIPaste PROMPT.md into Cursor, Lovable or v0.

Props

onCheckout
(items, total) → { ok }
data
store name, products, free-shipping line
autoplayDemo
false for a real shop

How qodesign compares

  • qodesign vs 21st.dev21st.dev is best for: browsing a huge marketplace and copying many components a month.
  • qodesign vs Skiper UISkiper UI is best for: unusual interaction details for landing pages: image reveals, cursor trails, tooltips, drag-and-scroll galleries.

All comparisons, with prices read on each library's own site.

Questions

Can I use Lemon Squeezy or Paddle instead?

Yes. The drawer only calls onCheckout. Make the checkout URL with their API in the server action.

Does it work without a server action?

Yes. An API route that returns the session URL works the same way.

Can Cursor or v0 add it for me?

Yes. Each download has a PROMPT.md. Paste it into Cursor, Lovable, v0 or Claude Code and it fits the piece into your app.

Can I use it in client work?

Yes, in personal and client projects. You can't resell the code itself as a kit.