Creem checkout in Next.js
An animated cart drawer that pays through Creem: a server action prices the cart, creates one Creem checkout session for the total and sends the buyer to Creem's hosted page.

By buying you agree to the Terms.
- Download right after paying
- Use it in client projects
- Won't run on the listed stack? We fix it or refund you
Ready forCursorv0Lovableshadcn CLI
Use it in Next.js
"use client";
import StackCartDrawer from "@/components/StackCartDrawer";
import { createCreemCheckout } from "./actions";
export default function Shop() {
return (
<div className="h-dvh">
<StackCartDrawer
autoplayDemo={false}
onCheckout={async (items) => {
try {
const url = await createCreemCheckout(items);
window.location.assign(url);
return { ok: true };
} catch {
return { ok: false }; // the drawer says the payment failed and keeps the cart
}
}}
/>
</div>
);
}"use server";
import { Creem } from "creem";
import { NW } from "@/data/northwind"; // the same product list the drawer shows
const creem = new Creem({
apiKey: process.env.CREEM_API_KEY!,
server: process.env.CREEM_MODE === "live" ? "prod" : "test",
});
export async function createCreemCheckout(items: { id: string; qty: number }[]) {
// Work the total out on the server from your own price list, never from the browser.
const lines = items.map((i) => {
const p = NW.shop.products.find((x) => x.id === i.id);
if (!p || !Number.isInteger(i.qty) || i.qty < 1) throw new Error("Unknown item in cart");
return { id: p.id, name: p.name, qty: i.qty, cents: Math.round(p.price * 100) * i.qty };
});
const subtotal = lines.reduce((sum, l) => sum + l.cents, 0);
const shipping = subtotal >= NW.shop.freeShipping * 100 ? 0 : Math.round(NW.shop.shipping * 100);
const checkout = await creem.checkouts.create({
productId: process.env.CREEM_ORDER_PRODUCT_ID!, // one one-time product, e.g. "Order"
customPrice: subtotal + shipping, // in cents, replaces that product's price for this session
successUrl: `${process.env.NEXT_PUBLIC_SITE_URL}/thanks`,
metadata: { cart: lines.map((l) => `${l.id}x${l.qty}`).join(",") },
});
if (!checkout.checkoutUrl) throw new Error("Creem sent no checkout URL");
return checkout.checkoutUrl;
}Good to know
- Type-checked against creem 1.13 and next 15.5 with TypeScript 5.9. We haven't run a live Creem checkout with it.
- A Creem checkout session takes one product id, plus units for a quantity. It has no list of products like some other providers. So the whole cart goes through one one-time product (call it Order) with custom_price set to the cart total.
- Creem's API reference says custom_price is in cents, per unit, and only works for one-time payment products. Creem's checkout page and receipt then show that one product's name, so the item list travels in metadata for your own records.
- The total is worked out on the server from the same product list the drawer shows. Never send a price from the browser. Anyone can change it there.
- server: "test" points the SDK at test-api.creem.io. Use a test mode key and product while you build, then switch the env var.
- Give access or ship the order from Creem's webhook (onCheckoutCompleted in @creem_io/nextjs), not from the /thanks page. Anyone can open /thanks without paying.
- Creem is the merchant of record and handles tax at checkout, so the total in the drawer is only a preview.
- If you sell one product or a subscription, you don't need a cart. Creem's own CreemCheckout button from @creem_io/nextjs takes a productId and is the simpler, free route.
Install
- Copy from the zipPut
react/src/components + react/src/dataintosrc/, thennpm i framer-motion creem. - 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
Why not one checkout per product?
Creem's checkout takes one product id per session, so a cart of three products would mean three payments. One Order product with a custom price keeps it to one.
Can I prefill the buyer's email?
Yes. Pass customer with the email in checkouts.create when you know who is signed in.
Does it work for subscriptions?
No. Creem only takes custom_price on one-time products. For plans, a pricing page with monthly and yearly fits better than a cart.
What if Creem answers with an error?
The action throws, onCheckout returns { ok: false } and the drawer says the payment failed. The cart stays as it was.
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.