The Ultimate Guide to Stripe + Next.js (2026 Edition)

Published: (February 24, 2026 at 02:36 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

1. The 2026 Lifecycle: Embedded vs. Hosted

Stripe now strongly pushes Embedded Checkout. Unlike the old redirect, it uses an iframe or a web component that lives inside your Next.js page, keeping your user on your domain while offloading all PCI compliance to Stripe.

2. Modern Project Setup

We’ll initialize a Next.js project with the App Router and the latest Stripe SDKs.

npx create-next-app@latest my-store-2026 --typescript --tailwind --app
cd my-store-2026
npm install stripe @stripe/stripe-js @stripe/react-stripe-js

3. Secure Environment Variables

Never expose your STRIPE_SECRET_KEY. In 2026, Next.js environment variables are strictly enforced.

# .env.local
STRIPE_SECRET_KEY="sk_test_51..."
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_51..."
STRIPE_WEBHOOK_SECRET="whsec_..."

4. The Server Action Pattern

In the 2026 docs, Server Actions are the standard for creating Checkout Sessions. This eliminates the need for /api/checkout folders.

Create src/app/actions/stripe.ts:

"use server";

import { stripe } from "@/lib/stripe";
import { headers } from "next/headers";

export async function createCheckoutSession(priceId: string) {
  const origin = (await headers()).get("origin");

  const session = await stripe.checkout.sessions.create({
    ui_mode: "embedded", // Enables the 2026 Embedded UI
    line_items: [{ price: priceId, quantity: 1 }],
    mode: "subscription",
    return_url: `${origin}/return?session_id={CHECKOUT_SESSION_ID}`,
  });

  return { clientSecret: session.client_secret };
}

5. Implementing Embedded Checkout

The EmbeddedCheckout component from @stripe/react-stripe-js provides a seamless “no‑redirect” experience.

Create src/components/CheckoutForm.tsx:

"use client";

import { loadStripe } from "@stripe/stripe-js";
import {
  EmbeddedCheckoutProvider,
  EmbeddedCheckout,
} from "@stripe/react-stripe-js";
import { createCheckoutSession } from "@/app/actions/stripe";

const stripePromise = loadStripe(
  process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
);

export default function CheckoutForm({
  priceId,
}: {
  priceId: string;
}) {
  // Fetch the clientSecret via the Server Action
  const fetchClientSecret = async () => {
    const { clientSecret } = await createCheckoutSession(priceId);
    return clientSecret as string;
  };

  return (
    
      
        
      
    
  );
}

6. Critical: The 2026 Webhook Handler

Even with Server Actions, Route Handlers are still required for Webhooks because Stripe needs a static URL to “ping” when a payment succeeds.

Create src/app/api/webhook/route.ts:

import { stripe } from "@/lib/stripe";
import { headers } from "next/headers";

export async function POST(req: Request) {
  const body = await req.text();
  const signature = (await headers()).get("stripe-signature")!;

  let event;

  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err: any) {
    return new Response(`Webhook Error: ${err.message}`, { status: 400 });
  }

  // Handle the event
  if (event.type === "checkout.session.completed") {
    const session = event.data.object;
    // 2026 Practice: Trigger a background sync or email service
    console.log(`💰 Payment confirmed for ${session.id}`);
  }

  return new Response(null, { status: 200 });
}

What’s New in the 2026 Stripe Docs?

  • Link Authentication Element: Automatically detects if a user has a “Link” account (Stripe’s 1‑click checkout) and pre‑fills their details, increasing conversion by up to 10%.
  • Adaptive Pricing: Now natively supported in Checkout Sessions—Stripe automatically shows the price in the user’s local currency based on their IP.
  • Enhanced Tax ID Collection: You can now toggle tax_id_collection: { enabled: true } directly in the session creation to handle global B2B tax compliance without extra logic.
  • Pro‑Tip: For local testing, use the Stripe CLI. Run stripe listen --forward-to localhost:3000/api/webhook to simulate successful payments on your local machine.
0 views
Back to Blog

Related posts

Read more »