Audit and optimize checkout UI extensions
Reduce checkout LCP and INP by identifying slow extensions, removing unused ones, and working with extension developers when a specific extension is the primary bottleneck.
Shopify checkout UI extensions load as JavaScript bundles on every checkout pageview, including extensions that are installed but not assigned to any active surface. Each extension adds network requests, JavaScript execution time, and potentially DOM nodes. An unused extension still initializes on load even if it renders nothing, so it costs download, parse, and execution time on every checkout. That cost is largest on mobile CPUs, and it varies with the extension, so measure it on your own checkout rather than assuming a figure.
The LCP element on checkout isn't available from RUM data. Instead, identify which extension is slowest to become visible to the buyer. Checkout instrumentation records two timings for each extension:
extension_visible: the time until the extension's content is visible on the page.extension_buyer_usable: the time until the extension can accept buyer input.
extension_visible is the relevant signal for LCP impact, not extension_buyer_usable. An extension that renders visible content late is a candidate for being the LCP element. extension_buyer_usable measures interactivity separately, so an extension can be visible and trigger LCP without being usable.
This applies to Shopify Plus merchants who use checkout UI extensions.
This applies to Shopify Plus merchants who use checkout UI extensions.
Anchor to Identify your slowest checkout extensionIdentify your slowest checkout extension
Work on a real checkout, not the checkout editor. The editor loads extra tooling and doesn't represent buyer conditions.
- Add a product to the cart and open the checkout in a Chrome incognito window, so extensions aren't served from a warm cache.
- Open DevTools, and in the Network panel select Disable cache, then set throttling to Slow 4G. Extension cost is hidden on a fast desktop connection.
- In the Network panel, filter on
extensionsand sort by Size, then by Time. Enable the Priority column. This gives you the per-extension bundle size and download time. - Reload the checkout with the Performance panel recording. Stop the recording after the checkout is fully rendered.
- In the recording, select the LCP marker to see which element is the LCP candidate. If it belongs to an extension block, then that extension is your LCP bottleneck.
- Look at the Main track for long tasks that start after the checkout skeleton paints. Expand each one, and note the script URL at the top of the flame chart. Extension work appears as its own script, separate from checkout's own bundles.
- Repeat on mobile emulation and on a real phone. Extension cost scales with CPU, so the ranking can differ between devices.
Rank extensions by the time they take to render visible content, not by bundle size alone. A small bundle that waits on a network request can render later than a large one that renders immediately.
Anchor to Triage with visibility versus usabilityTriage with visibility versus usability
Use the two signals to decide who fixes what and in which order:
- Visibility is the moment the extension's content appears on the page. A late-appearing extension is a candidate for the LCP element, and it's also what makes the checkout look unfinished. Triage these first.
- Usability is the moment the extension is wired up and can accept buyer input. An extension can be visible long before it's usable. Late usability shows up as INP and as buyers tapping a control that does nothing yet.
In practice:
- Content appears late, and the LCP element belongs to the extension: fix or remove the extension, or ask the developer to render a lightweight placeholder before fetching data.
- Content appears quickly, but interaction is unresponsive: the problem is main-thread work after render, not the bundle download. This is an INP problem, so measure it with the Performance panel while interacting, not on load.
- Content never appears: the extension failed to mount. It's still costing download and execution time for no buyer benefit, so remove it.
Blocks that render outside the initial viewport, such as an order status upsell, can't be the LCP element. Deprioritize them even when their bundle is large.
Blocks that render outside the initial viewport, such as an order status upsell, can't be the LCP element. Deprioritize them even when their bundle is large.
Anchor to Remove unused extensionsRemove unused extensions
In the Shopify admin, navigate to Settings > Checkout, and then click Customize. In the checkout editor, identify extensions with no active surface assignments. An extension that initializes but renders nothing still consumes main-thread time on every checkout load. Remove it if it's not necessary.
Anchor to Consolidate overlapping extensionsConsolidate overlapping extensions
Multiple extensions from different apps serving overlapping functions, such as two trust badge apps or multiple upsell blocks, add cumulative load cost. Evaluate whether you can achieve the same outcome with fewer installed extensions.
Anchor to Engage extension developers for slow extensionsEngage extension developers for slow extensions
If an extension is slow to become visible, then share timing data with the developer. The phase breakdown identifies where the time is spent: slow JavaScript download, slow parse and compile, or slow render. The developer controls the bundle size and render logic. These aren't changes the merchant can make directly.
Shopify doesn't publish a bundle-size limit for checkout UI extensions, so rank the bundles against each other on your own checkout. The largest bundle, and any bundle that's an outlier next to the rest, is the one to raise with its developer first.
Anchor to Identify which extension placement is slowestIdentify which extension placement is slowest
When one app has multiple checkout block instances, the performance impact varies by placement. Check which specific block placement has the slowest visibility time and review the content complexity of that block. It might be fetching external data or rendering more markup than others.
Anchor to ExamplesExamples
Anchor to List extension bundles by size and load timeList extension bundles by size and load time
Run this in the DevTools Console on a loaded checkout to get a sortable table of every extension asset the page fetched, with its transfer size and when it started:
Rank extension assets
DevTools Console
Two caveats. transferKB reports 0 for cross-origin assets served without timing headers, so compare decodedKB instead. Assets fetched inside an extension's sandbox don't appear in the top-level document's resource timeline, so if the table is empty or short, fall back to the Network panel filtered on extensions, which shows every request regardless of frame.
Record the table before and after each change. It's the cheapest before-and-after evidence you can hand to an app developer.
Anchor to Confirm where an extension is allowed to renderConfirm where an extension is allowed to render
Extension placement comes from the targets that the developer declares in the app's shopify.extension.toml. When you ask a developer why their extension loads on a step where you don't need it, this file is the answer:
Target assignment
shopify.extension.toml
Each target is a separate render surface with its own module. An extension that declares a checkout target and a Thank you target runs on both, so if you need only the post-purchase behavior, ask the developer to drop the checkout target rather than accepting the load cost on the highest-value page.
Anchor to TestingTesting
- Open the checkout in Chrome DevTools and check which element is the LCP candidate in the Performance panel. If it belongs to an extension block, then that extension is the first thing to fix or remove.
- Compare checkout LCP and INP before and after removing unused extensions. Test on a throttled mobile profile, not on desktop, and compare the same checkout step each time.
- Check extension bundle sizes in the Network panel, filtered on
extensions, and record them so that you have a baseline for the next audit. - Interact with each extension as soon as it appears. If it's visible but doesn't respond, then the gap between visibility and usability is the problem to report.
- Confirm that every extension mounts. An extension that downloads but never renders is pure cost.
- Rank blocks that render in the initial viewport ahead of blocks that render outside it in your remediation list.
Repeat the audit quarterly, and after installing or removing any checkout app. Each time, confirm that:
- Every installed checkout extension is assigned to an active surface. Uninstall the rest.
- No two extensions serve the same purpose, such as two trust badge blocks or two upsell blocks.
- No extension declares a checkout target when its value is only on the Thank you or Order status page.
- No extension declares the
network_accesscapability unless it demonstrably needs external data at checkout time. External calls add a serial dependency before content can render.
Anchor to ReferencesReferences
- Checkout UI extensions
- Configure app extensions
- Checkout extension capabilities
- Applying changes efficiently in checkout UI extensions
- Finding and ranking your worst JavaScript offenders
- Audit and remove third-party scripts
- Optimizing for Interaction to Next Paint (INP)