Search by

parkktech / fastmagento-checkout

FastMagento Checkout — makes the Magento 2 onepage checkout paint instantly. Renders a cacheable form shell with a skeleton, then hydrates window.checkoutConfig over an async request so the heavy per-quote config no longer blocks first paint. Optional add-on to FastMagento; works standalone on base

Maintainers

Package info

github.com/parkktech/FastMagentoCheckout

Homepage

Language:JavaScript

Type:magento2-module

pkg:composer/parkktech/fastmagento-checkout

Transparency log

Statistics

Installs: 6

Dependents: 0

Suggesters: 0

Stars: 5

Open Issues: 0

v1.0.0 2026-08-18 06:53 UTC

README

The instant, cacheable, islands-architecture checkout for Magento 2
Paints the moment the HTML lands. Types before the JavaScript finishes its coffee.

Magento PHP Base Magento only Every gateway Fails safe CSP License

Live demo

See it in production → add any product to your cart on DIYoffroad.com and watch the checkout paint instantly. Add-to-cart and checkout load are live, real-traffic demos.

Warning

⚠️ Not tested outside of ParkkTech FastMagento

This module has only been tested as a companion to the ParkkTech FastMagento extension. It may require FastMagento to work — or to deliver its full benefits — and is not yet proven standalone on base Magento. Until standalone validation is complete, treat it as a FastMagento add-on and use at your own risk.

👉 Get the extension it's built for: github.com/parkktech/FastMagento

Running this successfully without FastMagento? Please let us know so others can benefit — real-world standalone reports help us certify base-Magento support.

🐌➡️🚀 The Magento 2 onepage checkout has a dirty little secret: it isn't really "one page," it's "one giant blob of per-quote JSON your server sweats out before the browser can paint a single input." window.checkoutConfig shows up carrying every payment method, every totals breakdown, every region of every country — and until it arrives, your shopper stares at a spinner holding their credit card. FastMagento Checkout is what happened when we decided the shopper should be typing their email while all that runs in the background.

What it is. FastMagento Checkout re-architects the Magento checkout as islands: a static, instantly interactive contact + shipping form (real inputs in the first HTML byte — no skeleton, no spinner) surrounding the stock Knockout payment component, booted as an isolated island so every payment method you already have keeps working untouched (Stripe, Braintree, PayPal, Klarna, Amazon Pay, Vault, offline… plus ReCaptcha). The heavy per-quote window.checkoutConfig is deferred to an async endpoint and hydrated in the background, so the shell can be cached and the form is usable immediately. It runs on base Magento — no Hyvä, no Breeze, no theme surgery required — and if anything so much as sneezes, it fails safe to the stock checkout.

🚀 Quick install

composer require parkktech/fastmagento-checkout
bin/magento module:enable ParkkTech_FastMagentoCheckout
bin/magento setup:upgrade
bin/magento setup:di:compile        # production mode only
bin/magento cache:flush

# Fast checkout is ON by default. To fall back to the stock checkout at any time:
#   bin/magento config:set fastmagentocheckout/general/enabled 0
# or append ?fastcheckout=0 to a checkout URL for a one-off comparison.

⚠️ Running Hyvä? This module renders through the Luma/Knockout checkout.root block, which Hyvä's default theme does not output — without the free Luma-checkout fallback it stays inert and you simply get Hyvä's own checkout:

composer require hyva-themes/magento2-luma-checkout

Not sure what state you're in? Run bin/magento fastmagento:doctor (ships with parkktech/fastmagento) — it reports whether the fast checkout is enabled and whether the Hyvä fallback is present.

Stable since v1.0.0. No stability flags needed — a plain composer require resolves it. Verified end to end on Hyvä, Luma and Swissup Breeze: catalogue, product page, cart, checkout and a placed order on each.

Checkout style lives at Stores → Configuration → ParkkTech → FastMagento Checkout → Checkout Style, and ships as Single-Page Instant. To go back to the stock checkout, set it to Default (2-Step) or fastmagentocheckout/general/enabled to 0 — and ?fastcheckout=0 gives you a one-off stock render for comparison without changing any config. (Full options below.)

Requirements: Magento 2.4.6+, PHP 8.1+. That's it. No search engine, no special theme, no core patches.

🩺 Troubleshooting — bin/magento fastmagento:doctor

Installed but the checkout looks unchanged? Run the doctor (it ships with parkktech/fastmagento) — it reports this module's state explicitly instead of leaving you to guess:

bin/magento fastmagento:doctor
CHECKOUT
  ✓ Fast checkout                      enabled
  ✓ Hyvä checkout compatibility        Hyva_LumaCheckout enabled
  ✓ Checkout theme static content      frontend/Magento/luma deployed for en_US

It tells you whether the fast checkout is actually switched on, and — because this module renders through the Luma/Knockout checkout.root block that Hyvä's default theme does not output — whether the free hyva-themes/magento2-luma-checkout fallback is present on any store view running Hyvä. Those are the two reasons a correct install still shows the stock checkout.

The third check is the one nobody thinks to look for; it has its own section below.

🎨 Your logo follows the shopper into checkout

Theme fallback renders checkout in a different theme, and Magento only reaches for a theme's own images/logo.svg as a last resort — resolved against whichever theme is rendering. So a store that has never uploaded a logo shows its storefront theme's mark on every page, then the fallback theme's stock mark at the moment of payment. On a Breeze storefront that is a literal "BREEZE" wordmark everywhere and a "LUMA" one at checkout.

This module resolves the same asset against the theme the store is configured to use, so the fallback theme borrows the storefront's logo and nothing else changes. Your themes keep their own branding; only the checkout follows.

It deliberately does nothing when a logo has been uploaded (design/header/logo_src) — that is already theme-independent and the merchant's explicit choice outranks ours — when no theme swap is in play, or when the configured theme ships no logo of its own.

⚠️ On Hyvä, deploy the fallback theme's static content too

This is the single most confusing failure this module has, and it is not a bug in the module.

hyva-themes/magento2-luma-checkout brings Hyva_ThemeFallback, which swaps the design theme at runtime for the configured URL segments — /checkout/index among them. So your checkout renders in a different theme (Magento/luma by default) from the rest of your storefront, and that theme needs its own deployed static content.

When it is missing, checkout returns a perfectly healthy HTTP 200 while every Luma CSS, JS, font and image 404s underneath it. RequireJS is one of those 404s, so the Knockout checkout never boots either. What you see is an unstyled wall of browser-default text — which looks exactly like a broken module, and isn't one.

It is easy to arrive at by accident. A full deploy that dies on some unrelated broken theme can abort before it reaches the fallback theme, and the natural workaround — pinning the deploy to your storefront theme — skips the fallback theme too, every single time:

# ✅ deploys your theme AND the checkout fallback theme
bin/magento setup:static-content:deploy -f -a frontend --exclude-theme Vendor/broken-theme en_US

# ❌ silently skips Magento/luma — catalogue styled, checkout not
bin/magento setup:static-content:deploy -f -a frontend --theme Your/theme en_US

Quick check, any time:

ls pub/static/frontend/Magento/luma/en_US/css/styles-m.css

If that is missing — or the directory holds only requirejs-config.js, which Magento writes there at runtime and which therefore proves nothing — the fallback theme was never deployed. fastmagento:doctor reports exactly this, per store view and locale, with the deploy command to fix it.

One more thing worth knowing: once theme fallback is enabled it applies globally, so a Luma or Breeze storefront routes /checkout/index through the fallback too — not just Hyvä.

🤝 The perfect pairing

FastMagento Checkout is a proud member of the FastMagento family and the natural other half of its story:

FastMagento obliterates the data cost. FastMagento Checkout obliterates the checkout wait.

FastMagento makes OpenSearch the primary serving layer for Magento — catalog, category tree, PDP, search and layered navigation served in milliseconds, MySQL still the source of truth, real product objects, FPC/Varnish-safe, on base Magento. It even makes add-to-cart fast by killing the ~30× inventory-table loop.

Run them together and you've got the whole funnel covered:

Stage Native Magento With the pair
Browse / search / PDP thousands of EAV queries ⚡ FastMagento → 0 product/EAV SQL, millisecond pages
Add to cart loops inventory tables ~30× ⚡ FastMagento → flat, can't oversell
Checkout spinner until checkoutConfig lands 🛒 FastMagento Checkoutinstant paint, type immediately

They're independent — each is a standalone win — but together they're a banger of a store: lightning data layer and a checkout that paints before your shopper can blink. 🎸

👉 Get the other half: composer require parkktech/fastmagento

Why it exists (the honest origin story)

We had a store that was fast everywhere except the last step. Catalog flew, search was instant, cart was flat — and then the shopper hit checkout and got the classic Magento spinner of contemplation while the server assembled a wall of JSON. The frustrating part? None of that JSON is needed to let someone start typing their name. The email field, the street, the city — those are static. Only the payment methods and totals need the private per-quote config, and even those don't need to block first paint.

So we split the checkout into islands: paint the static form instantly, boot the real payment component as its own little app, and fetch the private config in the background. The result is a checkout that feels like a well-built SPA while remaining 100% stock Magento payment code — because the fastest checkout is the one you don't have to babysit.

Problems it solves (problem → solution)

  • Problem: window.checkoutConfig blocks first paint; the shopper waits on a spinner to type an email. Solution: a static, immediately-typeable form ships in the first HTML byte; the heavy config loads async and hydrates in place.
  • Problem: the private per-quote config makes the checkout page uncacheable. Solution: the shell carries only static markup + a tiny safe skeleton; the per-quote data is fetched from a dedicated endpoint → the shell is cache-friendly.
  • Problem: custom checkouts break your payment methods and you re-integrate Stripe/PayPal/Braintree/ReCaptcha by hand. Solution: we boot the real stock Knockout payment subtree as an island — every installed method + ReCaptcha work unchanged, zero per-method code.
  • Problem: logged-in shoppers re-type an address Magento already has, and fetching it hits the DB on every checkout. Solution: the address book rides in the customer-data section (localStorage) — the default address prefills and a carousel lets them pick another, with no SQL on the checkout page.
  • Problem: shoppers don't know their postcode format / skip the region and rates never load. Solution: Google Places autocomplete on the street field fills city/state/ZIP/country (region dropdown matched for you) — CSP whitelist baked into the module.
  • Problem: a guest orders today, registers next month, and their history is orphaned. Solution: guest orders link to a matching account at place-time, and a new registration retroactively claims every past guest order for that email.
  • Problem: the order summary is a cramped text list with no images and no idea when to scroll. Solution: product thumbnails (configurable → chosen simple, parent fallback) and an items list that scrolls at the smaller of your "max items" setting and the payment form's height.
  • Problem: autocomplete skips the state field and the shopper never notices until "Place Order" fails. Solution: inline validation flags empty required fields on blur and when they focus a later field, and the shipping box says exactly what's missing.
  • Problem: you flip on a fancy checkout and pray it doesn't break on a bad day. Solution: fail-safe by design — an admin toggle, a per-request ?fastcheckout=0 kill-switch, and automatic redirect to stock checkout if the config can't load. No schema changes. Rollback is instant.

✨ Features

🏝️ Islands architecture — instant paint, stock payments

The contact + shipping form is plain, static HTML mounted by a vanilla-JS widget (runs identically under stock Magento and Breeze). The payment area is the real Magento Knockout component, booted into its own node via Magento's own Magento_Ui/js/core/app. You get an instant frontend and the full, battle-tested payment stack — Braintree, Stripe, PayPal, Klarna, Amazon Pay, Vault, offline — with no per-method integration.

⏳ Deferred checkoutConfig (the whole trick)

The private per-quote config is fetched from fastcheckout/config after first paint and deep-merged in place into a tiny static skeleton — preserving object identity so eager third-party modules (looking at you, Stripe minicart) that read window.checkoutConfig at eval time don't throw. The shell has no private data inlined, so it's cache-friendly.

🏠 In-memory saved addresses + carousel (zero SQL on checkout)

Logged in? Your address book is delivered once via a customer-data section and cached in localStorage. On checkout it prefills your default address (and email) and shows a swipeable carousel of every saved address — plus a leading "+ New address" card that clears the form to type a fresh one. Picking a card repopulates every field, region dropdown included. The section refreshes only on login / address change / order placed — so a returning shopper hits checkout with no per-visit database call.

📍 Google Places address autocomplete

Start typing your street and pick a suggestion — city, state, ZIP and country fill themselves (the region dropdown is matched by code, not just text). Reuses an existing store Google key if you have one (e.g. Amasty Address Autocomplete) or takes its own. The Google Maps hosts are in a self-contained csp_whitelist.xml shipped with the module, so it works even if your site has no CSP module of its own.

🔗 Guest orders that find their home — in both directions

  • At checkout: a guest whose email already belongs to an account → the order is attached to that account (so it shows in their history immediately).
  • At registration: a brand-new account retroactively claims every past guest order placed with that email.

One toggle, on by default. (It links by email match without a login step — so it's a toggle, not a mandate. Off = every guest order stays a guest.)

🧾 Order summary that earns its keep

Real product thumbnails (for configurables: the chosen simple's image, falling back to the parent), tidy totals from the live quote, and an items list that scrolls internally once it would either exceed your Maximum Items to Display setting or run past the bottom of the payment form — whichever comes first. It never shoves your "Place Order" button off-screen.

🚦 Inline validation + honest hints

Empty required fields turn red on blur — and when you focus a later field, we flag the one your browser's autofill quietly skipped. The shipping box tells you exactly what's missing ("Please select your state above") instead of silently refusing to show rates.

🧩 Physical, virtual & mixed carts

Physical carts estimate rates the instant the address is complete and keep Magento's native "same as shipping" billing toggle. Virtual/downloadable carts hide shipping and treat the form as the billing address. Mixed carts do both. All three place real orders.

🎨 Theme-independent styling

Sizing is in absolute px, so it doesn't shrink into oblivion on Luma themes that set html { font-size: 62.5% }. Palette is a clean slate + go-green accent; responsive down to phones.

🔧 Configuration

Stores → Configuration → ParkkTech → FastMagento Checkout

Setting Default What it does
Checkout Style Default (2-Step) Single-Page Instant turns on the fast checkout. Ships off so installing changes nothing until you choose.
Address Autocomplete → Enable Yes Google Places suggestions on the street field. Only activates when a key resolves.
Address Autocomplete → Google Maps API Key (blank) Plain text (a client-side Maps key is public — restrict it in Google Cloud). Blank = reuse the Amasty Address Autocomplete key if present.
Link Guest Orders to Existing Accounts Yes Attach guest orders to a matching account at checkout, and claim past guest orders when that email registers.

Per-request escape hatch: append ?fastcheckout=0 to /checkout to force the stock checkout for that one request (handy for support and A/B sanity checks).

🛠️ How it works (for the curious)

                          ┌─────────────────────────────────────────────┐
  First HTML byte  ─────► │  Static contact + shipping form (typeable)   │  ← instant paint
                          │  Order summary (from customer-data cart)     │
                          └───────────────┬─────────────────────────────┘
                                          │  async, after paint
                                          ▼
                          GET fastcheckout/config  ──►  window.checkoutConfig
                                          │  deep-merge (identity-preserving)
                                          ▼
                          ┌─────────────────────────────────────────────┐
   Payment island ──────► │  Real Magento KO payment subtree, booted via │
                          │  Magento_Ui/js/core/app  (all gateways)      │
                          └─────────────────────────────────────────────┘
  • Kill-switch first. Disabled or ?fastcheckout=0 → the template renders byte-for-byte stock onepage markup. The fast path is opt-in and reversible.
  • Address book in memory. A fastcheckout-customer customer-data section carries the address book to localStorage; invalidated only on login/logout/address-save/order-success.
  • Order linking runs on sales_order_place_before (place-time) and customer_register_success (retroactive) — registered globally, because Magento places orders through the Web API, not the frontend area.

❓ FAQ

Does it replace my payment methods? No. It boots the real Magento payment component — your existing methods and ReCaptcha work exactly as they do on stock checkout.

Do I need Hyvä or Breeze? No. Base Magento Luma is fine. It also runs happily under Breeze.

Is it safe to install on a live store? Yes — it's off by default, makes no schema/data changes, and fails safe to stock checkout. Turn it on when you're ready; turn it off instantly if you're not.

What if the async config fails? After a couple of retries the shopper is transparently redirected to the guaranteed-stock checkout. Nobody gets stranded.

Does the saved-address feature hit the database on every checkout? No — that's the point. It's served from localStorage; the DB is touched only when the address book actually changes.

↩️ Rollback

Set Checkout Style → Default (2-Step), or bin/magento module:disable ParkkTech_FastMagentoCheckout. No schema or data changes were ever made.

📦 Also by ParkkTech

  • FastMagento — OpenSearch serving layer & large-catalog toolkit. The perfect pairing (see above). composer require parkktech/fastmagento

📄 License

OSL-3.0 / AFL-3.0. Built by ParkkTech.