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.

Use it in Next.js
"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>
);
}"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
- Copy from the zipPut
react/src/components + react/src/dataintosrc/, thennpm i stripe. - 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.