Polar checkout in Next.js
The free pricing switch sends the chosen plan and billing period to Polar's Next.js checkout route, which redirects the buyer to Polar to pay.
Use it in Next.js
"use client";
import PricingSwitch, { type Plan } from "qodesign-free/pricing-switch";
const plans: Plan[] = [
{ id: "starter", name: "Starter", description: "For side projects.", monthly: 9, yearly: 7, cta: "Start Starter", features: ["1 project", "Email support"] },
{ id: "pro", name: "Pro", description: "For a product with paying users.", monthly: 29, yearly: 23, cta: "Start Pro", features: ["10 projects", "Priority support"], featured: true },
];
// One Polar product per plan and billing period, made in your Polar dashboard (sandbox first).
const POLAR_PRODUCTS: Record<string, { monthly: string; yearly: string }> = {
starter: { monthly: "POLAR_PRODUCT_ID_STARTER_MONTHLY", yearly: "POLAR_PRODUCT_ID_STARTER_YEARLY" },
pro: { monthly: "POLAR_PRODUCT_ID_PRO_MONTHLY", yearly: "POLAR_PRODUCT_ID_PRO_YEARLY" },
};
export default function PricingPage() {
return (
<main className="mx-auto max-w-4xl px-4 py-16">
<PricingSwitch
plans={plans}
header={<h1 className="text-3xl font-semibold">Pricing</h1>}
yearlyBadge="2 months free"
onSelect={(plan, yearly) => {
const ids = POLAR_PRODUCTS[plan.id];
if (!ids) return;
window.location.assign(`/checkout?products=${yearly ? ids.yearly : ids.monthly}`);
}}
/>
</main>
);
}import { Checkout } from "@polar-sh/nextjs";
// GET /checkout?products=<id> creates a Polar checkout and redirects the browser to it.
export const GET = Checkout({
accessToken: process.env.POLAR_ACCESS_TOKEN!,
successUrl: `${process.env.NEXT_PUBLIC_SITE_URL}/welcome`, // Polar appends checkout_id={CHECKOUT_ID}
environment: process.env.NODE_ENV === "production" ? "production" : "sandbox",
});Good to know
- Tested in a fresh Next.js 16.4.2 app with @polar-sh/nextjs 1.1.0. Start Pro sent the browser to /checkout?products=<the Pro monthly id>, and with Yearly on, Start Starter sent the yearly id. GET /checkout with no products answered 400, as Polar's docs say.
- We had no Polar account in the test, so the hop into Polar's own checkout page wasn't run. With an invalid access token the route answered 500 and the server log showed Polar's invalid_token error, so if the button lands on an error page, check the token and the environment first.
- successUrl must be an absolute URL. Polar adds checkout_id={CHECKOUT_ID} to it.
- The switch shows the numbers in plans; Polar charges what the product says. Change both together.
- To give access after payment, use the Webhooks handler from the same package (onOrderPaid, onSubscriptionActive).
Install
- One command
npm i qodesign-free framer-motion @polar-sh/nextjs - Or ask your AIPaste PROMPT.md into Cursor, Lovable or v0.
Props
- plans
- name, monthly, yearly, features, cta
- onSelect
- (plan, yearly) when a button is pressed
- yearlyBadge
- text on the switch, e.g. 2 months free
- currency
- symbol shown
How qodesign compares
- qodesign vs 21st.dev21st.dev is best for: browsing a huge marketplace and copying many components a month.
- qodesign vs Magic UIMagic UI is best for: landing pages built with shadcn/ui.
All comparisons, with prices read on each library's own site.
Questions
Is the pricing switch free?
Yes. MIT, from the free npm package or the shadcn registry.
Can I pass the buyer's email?
Yes. The checkout route reads customer_email from the query string, so add &customer_email=... to the URL in onSelect.
Can Cursor or v0 add it for me?
Yes. The folder has a PROMPT.md. Paste it into Cursor, Lovable, v0 or Claude Code.
Does the pricing switch work in Next.js?
Yes, as a client component. Keep "use client" at the top of the file that renders it, as in the snippet above. App Router and Pages Router both work.