SETSimple Easy Transactions
STRIPE BUYER'S GUIDE

Stripe Embedded Checkout vs Hosted Checkout

By , Founder of Simple Easy Transactions · Published ·

Should I use Stripe's embedded Checkout or the Stripe-hosted Checkout page? If the customer should stay on my site, I use embedded Checkout: my server starts a Checkout Session and Stripe's payment form appears inside my page. If I want the fewest moving parts, I send them to the Stripe-hosted page, which Stripe runs from start to finish. Both are the same Stripe Checkout underneath, and both follow the same rule: nothing ships until Stripe's signed webhook confirms the payment.

AT A GLANCE

Embedded Checkout vs Stripe-hosted Checkout.

QuestionEmbedded CheckoutStripe-hosted Checkout
Where the customer paysInside your page, in a secure Stripe frameOn a Stripe page, after a redirect
What your server doesStarts a Checkout Session and gives your page the key it needs to show the formStarts a Checkout Session and sends the customer to its link
Work on your siteAdd Stripe's script and an empty spot on the page for the formA button or a link, nothing else
Look and feelYour page around it, your Stripe brand settings inside itStripe's page with your logo and colors
After payingStripe brings the customer back to your return pageStripe sends the customer to your success page
What proves the paymentStripe's signed webhookStripe's signed webhook

HOW EACH ONE WORKS

How each one works.

Both options are Stripe Checkout: the same prebuilt payment page, with the same payment methods, discounts, taxes and receipts. The only real question is where that page lives. Stripe's documentation calls it the full-page option and lets you either redirect to it or embed it on your own site.

With the hosted page, your server creates a Checkout Session and sends the customer to the link Stripe returns. Stripe shows the page, takes the payment and sends the customer back to the success page you named. It's the least code you can write and still get a complete checkout.

With embedded Checkout, your server creates the same session in embedded mode and returns a client secret, a one-time key for that checkout. Your page loads Stripe.js and places Checkout in an empty container, and the form runs inside a secure Stripe frame, so card details go straight to Stripe and never touch your server. Stripe's guide warns against putting that form inside another frame, because some payment methods have to redirect to confirm.

There's a third way worth knowing: Checkout Sessions with Stripe Elements, where you place Stripe's payment fields inside a form you design yourself. It takes the most work and gives the most control; the Elements guide covers when that's worth it.

The 2026 renames

initEmbeddedCheckout, EmbeddedCheckoutProvider and the new names.

If you searched for initEmbeddedCheckout, the method has a new name. Stripe's Dahlia release of March 25, 2026 renamed stripe.initEmbeddedCheckout() to stripe.createEmbeddedCheckoutPage(). Stripe's changelog says the options and return type stay the same, and that calling the old name now throws an IntegrationError.

The Checkout Session changed with it. Its ui_mode values are now hosted_page, the default; embedded_page for embedded Checkout; elements for Checkout elements on your own page; and form for the Checkout form. Stripe's changelog says a session set to the old values hosted, embedded or custom will fail.

In React, Stripe's embedded Checkout quickstart still uses EmbeddedCheckoutProvider from @stripe/react-stripe-js: you pass it the promise from loadStripe and an options object with fetchClientSecret. Stripe's Stripe.js reference marks the older clientSecret option as deprecated in favour of fetchClientSecret, and calls onComplete only for sessions created with redirect_on_completion set to if_required.

The Elements side was renamed too: initCheckout() is now initCheckoutElementsSdk(), and CheckoutProvider is now CheckoutElementsProvider. A session with ui_mode form uses initCheckoutFormSdk() instead.

Two rules did not change. The Checkout Session is created on your server, because creating one needs your secret key; and Stripe's fulfillment guide says to use webhooks so that every payment is fulfilled, not only the ones whose customer reaches the return page.

  1. Create the Checkout Session with ui_mode embedded_page instead of embedded.
  2. Call stripe.createEmbeddedCheckoutPage({ fetchClientSecret }) instead of stripe.initEmbeddedCheckout().
  3. Mount it with checkout.mount("#checkout"); to show a new session, destroy the old instance first and create a new one.
  4. Keep fulfillment on the signed webhook, not the return page.
The two calls with the 2026 names (Node on the server, Stripe.js in the browser).
// Server: the secret key never leaves it
const session = await stripe.checkout.sessions.create({
  ui_mode: "embedded_page",
  mode: "payment",
  line_items: [{ price: "price_123", quantity: 1 }],
  return_url: "https://example.com/return?session_id={CHECKOUT_SESSION_ID}",
});
// Send session.client_secret to the page.

// Browser: Stripe.js loaded from https://js.stripe.com/dahlia/stripe.js
const checkout = await stripe.createEmbeddedCheckoutPage({ fetchClientSecret });
checkout.mount("#checkout");

FIELD NOTES

What I've learned building this.

  • This site's own checkout uses that third way. Open Payment lets the customer type the amount, and I wanted that field right next to Stripe's payment fields, so I used a Checkout Session with Stripe's Payment Element inside my own form.
  • Stripe won't take a pay-what-you-want price in that setup, so I build the charge from the amount the customer typed and attach it to one saved product. Otherwise every payment would add a new product to the catalog.
  • When a Stripe call fails, answer with your own error message. Mine once let the error escape; the server sent back an empty page, and the buyer saw a cryptic parser error instead of a sentence.
  • My return page only says thank you. The order is recorded when Stripe's signed webhook arrives, and it's stored so that a repeated delivery can't record it twice.
  • The $49 Embedded Checkout Starter Kit sold on this site is written against the new names: its Worker creates the session with ui_mode embedded_page, and its page loads Stripe.js from js.stripe.com/dahlia and calls stripe.createEmbeddedCheckoutPage with fetchClientSecret.

HOW TO CHOOSE

How I'd choose.

  • Choose embedded Checkout when leaving your site would break the flow, such as paying inside an app, a members area or a multi-step booking.
  • Choose the hosted page when you want the least code and a short trip to Stripe is fine for your customers.
  • Choose Checkout Sessions with Elements when the payment fields have to sit inside a form you already own.

QUESTIONS

Questions people ask.

Why does stripe.initEmbeddedCheckout throw an IntegrationError?

Stripe renamed it to stripe.createEmbeddedCheckoutPage in its Dahlia release of March 25, 2026, and its changelog says calling the old name throws an IntegrationError. The options and return type are the same, so the fix is the new method name, plus ui_mode embedded_page on the Checkout Session.

What ui_mode does Stripe embedded Checkout use?

embedded_page. Stripe's changelog lists hosted_page, embedded_page, elements and form as the current values and says a session set to the old values hosted, embedded or custom will fail.

Is EmbeddedCheckoutProvider still used in React?

Yes. Stripe's embedded Checkout quickstart passes the promise from loadStripe and an options object with fetchClientSecret to EmbeddedCheckoutProvider from @stripe/react-stripe-js. On the Elements side, CheckoutProvider was renamed to CheckoutElementsProvider.

SOURCES

Where these facts come from.

Sources checked on . Providers change their products and rules, so the linked pages are the final word.

KEEP READING

PAYMENTS BY STRIPE

The Stripe documentation behind this page.

SET Connect, SET's payment builds and the card onramp in SET Wallet run on Stripe. Stripe's own documentation is the final word on how its part works. Links checked on .

Simple Easy Transactions is a separate company from Stripe. It uses Stripe's services under its own account; Stripe does not endorse SET.