Next.js + PayRequest

Add a payment button to your Next.js app

The Next.js docs hand you a 2,000-line Stripe Elements walkthrough. You don't need that. Two lines of HTML, an anchor and a stylesheet — no SDK, no API route, no client component required.

Payment button for Next.js apps
The wall

Why Stripe in Next.js is harder than it should be

Next.js App Router is built around React Server Components, but Stripe.js is a client-only SDK. The moment you reach for it you have to mark a component "use client", load a heavy script, juggle a publishable key, set up an API route to create payment intents, build a webhook handler to confirm them, and then style a checkout that almost-but-not-quite matches your brand. For the 90% of Next.js apps that just want a Pay button on a marketing page, this is laughably overkill.

  • Stripe.js forces a 'use client' boundary, breaking your App Router server tree.
  • Client SDKs ship 90KB+ of JavaScript — Lighthouse hates it.
  • API route + webhook + secret rotation + retry logic, just for one button.
  • Stripe Checkout is hosted, but the redirect URL is opaque and the styling lives in the dashboard, not your codebase.
The fix

The fix: a static anchor that lands on a branded checkout

PayRequest's Payment Button is a plain HTML link styled by a single CSS file. It renders inside a Server Component (no "use client" needed), ships zero JavaScript to the browser, and respects every CSP rule you've set. Customers click the anchor and land on your branded PayRequest checkout — handle, colours, payment methods all served from your dashboard.

Renders in a Server Component

It's an <a> tag and a <link>. No 'use client', no useState, no client-side hydration cost.

Zero JavaScript on your bundle

The button adds 0 bytes to your Next.js JS bundle. The stylesheet loads from PayRequest's CDN — separate from your build.

No API routes, no webhooks

Payment intent creation, confirmation, fulfilment emails and dunning all happen on PayRequest. Your /api folder stays empty.

App Router, Pages Router, both fine

Same snippet works in app/, pages/, route handlers, server actions — anywhere you can render HTML.

The snippet

The whole integration, in two lines of HTML

Paste the <link> in your root layout.tsx (or app/layout.tsx) <head>. Drop the <a> wherever you want a button — in any RSC, MDX file, or layout fragment.

app/layout.tsx
// app/layout.tsx — once per project
<head>
  <link rel="stylesheet"
        href="https://payrequest.app/embed/button.css" />
</head>

// any Server Component (app/page.tsx, MDX, etc.)
<a href="https://payrequest.me/yourhandle/pro-plan"
   className="pr-btn pr-btn--default"
   target="_blank"
   rel="noopener">Pay €19</a>

Three button styles ship with the stylesheet — pick one to match your Next.js theme.

Walkthrough

Step-by-step: from npm install to live payment

01

Create a Smart Link in PayRequest

Sign in → Payment Page → Smart Links → New. Set the amount, pick your methods (card, Apple Pay, iDEAL, PayPal), copy the URL.

30 sec
02

Add the stylesheet to app/layout.tsx

Inside your root <head>, drop a <link rel="stylesheet" href="https://payrequest.app/embed/button.css" />. Once per project — every page picks up the styles.

10 sec
03

Drop the anchor in any page or component

Pricing page, hero, blog post — wherever the CTA lives. The anchor is a server-renderable <a> with a className. No client wrapper required.

5 sec
04

Set the href to your Smart Link

Use an environment variable if you want to swap test/live URLs: process.env.NEXT_PUBLIC_PAYREQUEST_LINK. Treat it like any other public URL.

10 sec
05

Deploy to Vercel

vercel --prod (or your usual flow). The button is static HTML — Vercel caches it, the stylesheet loads from PayRequest's CDN, and the click hands off to your branded checkout. Done.

Live
Comparison

Next.js + Payment Button vs the alternatives

MethodSetup timeClient JSAPI routesBranded checkoutPayment methods
Stripe Elements
Half a day~90 KB2+ requiredManual stylingConfigurable
Stripe Checkout (redirect)
1–2 hoursStripe.js (~90 KB)1 + webhookLimitedCard + wallets
PayPal Buttons SDK
1 hour~150 KB1 (capture)PayPal-brandedPayPal + cards
PayRequest Payment ButtonBest
1 minute0 KBNoneYours20+ including iDEAL, SEPA, Klarna
What to ship

What Next.js teams ship with the Payment Button

Pricing pages on marketing sites

RSC-rendered pricing tier with a Pay button per plan. No client component, no API route, no boilerplate.

MDX blog posts and tutorials

Drop the <a> directly into MDX content. Sell prompt packs, courses or tip jars from any post without ejecting to a custom layout.

Server actions + payment confirmations

Use server actions to create the Smart Link on the fly, then render the button server-side. Full RSC stream, no client hydration penalty.

Static blogs and documentation

Next.js export-mode sites and docs.your-domain.com get a real payment CTA without bringing the runtime back.

Under the hood

Why this beats every Stripe-in-Next.js tutorial

Read any "Stripe with Next.js App Router" article and you'll find at least 12 files: a server action, a pages route, a webhook handler, a success page, a metadata file, a client wrapper, a price-id constants file, a Stripe utility, a webhook signing helper, a customer-portal redirect... and a comment explaining what got broken when Stripe moved to Stripe.js v3. The Payment Button replaces all of that with a CSS class.

  • Works in app/, pages/, MDX, route handlers, server actions, edge runtime — same snippet, everywhere.
  • No publishable or secret keys in your repo. The link is the integration.
  • CSP-friendly: same-origin <a>, no inline scripts, no third-party iframes.
  • Provider-agnostic: switch between Stripe, Mollie, PayPal or Ponto on PayRequest without redeploying Next.js.
FAQ

Frequently asked questions

Do I need to mark the component "use client" to use the Payment Button?+
No. The Payment Button is a plain HTML anchor. It renders inside a Server Component without any client boundary. Your RSC tree stays clean and your bundle stays small.
Where do I put the stylesheet — in app/layout.tsx or every page?+
Once in app/layout.tsx is enough. Drop a <link rel="stylesheet" href="https://payrequest.app/embed/button.css" /> inside the <head> and every page in the App Router picks up the styles automatically. You don't need to add it per route.
Will the button hurt my Lighthouse scores?+
No. The stylesheet is a few KB gzipped, served from PayRequest's CDN, and there's no JavaScript to parse. We've seen Lighthouse Performance go up after replacing Stripe.js because there's no async script blocking the main thread anymore.
Can I use the Payment Button with Tailwind classes for layout but keep the PayRequest styles?+
Yes. The stylesheet only styles the .pr-btn classes. Wrap the button in any Tailwind container — flex, grid, hero — and the anchor inherits the layout while keeping its PayRequest visual styling.
Does this work on Vercel's Edge runtime?+
Yes. The whole integration is static HTML — there's no runtime code to execute on edge or node. The page renders identically on every Next.js deployment target.
How do I track conversions from a specific Next.js page?+
Every click feeds the Smart Link's view, conversion and revenue metrics. To attribute them per source, append a query string (?utm_source=pricing) and PayRequest groups conversions by parameter — without you adding any tracking script.
Next.js + PayRequest

Ship payments in your Next.js app today

Sign up, create a Smart Link, paste the snippet into app/layout.tsx and any page. Five minutes, zero SDK, real money.