Shopify Mobile
October 23, 2026Shopify Checkout Kit in React Native: A Production Setup
Checkout Kit is how a custom Shopify app takes payment without rebuilding checkout. The official README gets you to a working demo. This guide covers what it leaves to you: the cart, when to preload, expired carts, and the Android setting that silently breaks payment apps. Everything here was checked against version 3.9.1 of the package.
How it fits together
Your app owns the cart. You create it and add lines with the Storefront API, which returns a checkoutUrl.
Checkout Kit owns checkout. You pass it the checkoutUrl, and it presents Shopify's checkout in a native sheet.
Shopify owns the order. Payments, taxes, and order creation run on Shopify, and your app hears about the result through events.
Requirements
| Package | @shopify/checkout-sheet-kit (3.9.1 when this was written) |
| React Native | 0.76+ for 3.9.x and 4.x. 0.70+ for 3.8.x and earlier. |
| Architecture | 3.9.x: old or New Architecture. 4.x: New Architecture only. |
| iOS | The kit needs iOS 13, but React Native 0.76 itself needs iOS 15.1 |
| Android | The kit needs minSdkVersion 23, but React Native 0.76 needs 24 and JDK 17 |
| Shopify | A Storefront API access token. Plus only for branding, Multipass, and UI extensions on the information, shipping, and payment steps. |
Two version notes. If your app is still on the old architecture, stay on 3.9.x until you migrate, because 4.x requires the New Architecture. And Shopify is renaming “Checkout Sheet Kit” to “Checkout Kit,” with the package name changing in an upcoming version, so expect one import rename later.
1. Install and wrap the app
Install the package and its iOS pod. The kit's own floor is iOS 13 and Android API 23, but React Native 0.76 already requires iOS 15.1, Android API 24, and JDK 17, so an app on a supported React Native version clears the kit's bar.
npm install @shopify/checkout-sheet-kit
cd ios && pod installWrap the app in the provider. ColorScheme.automatic follows the device's light or dark mode. ColorScheme.web uses the branding configured in your Shopify admin instead.
import {ColorScheme, ShopifyCheckoutSheetProvider} from '@shopify/checkout-sheet-kit';
export default function Root() {
return (
<ShopifyCheckoutSheetProvider configuration={{colorScheme: ColorScheme.automatic}}>
<App />
</ShopifyCheckoutSheetProvider>
);
}2. Build the cart with the Storefront API
Checkout Kit only presents checkout. Getting a checkoutUrl is your job. The README's example uses Apollo and pins Storefront API version 2025-07, which Shopify now lists as unsupported. Versions are supported for at least 12 months, so pin a current one and put the upgrade on your calendar. A plain fetch is enough:
const SHOP = 'your-store.myshopify.com';
const API_VERSION = '2026-10'; // check it is still supported before you ship
const STOREFRONT_TOKEN = '...'; // the public Storefront API token
async function storefront<T>(query: string, variables: Record<string, unknown>): Promise<T> {
const res = await fetch(`https://${SHOP}/api/${API_VERSION}/graphql.json`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': STOREFRONT_TOKEN,
},
body: JSON.stringify({query, variables}),
});
const json = await res.json();
if (json.errors) throw new Error(json.errors[0].message);
return json.data;
}
const CART_CREATE = `
mutation CartCreate($lines: [CartLineInput!]!) {
cartCreate(input: {lines: $lines}) {
cart { id checkoutUrl updatedAt }
userErrors { field message }
}
}
`;
const CART_LINES_ADD = `
mutation CartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart { id checkoutUrl updatedAt }
userErrors { field message }
}
}
`;Always read userErrors. A cart mutation can succeed at the HTTP level and still reject a line, for example when a variant is out of stock. Store the cart ID on the device so the cart survives an app restart.
3. Preload on the cart screen, not on add-to-cart
Preloading starts the checkout session in the background so the sheet opens without a spinner. Shopify is specific about when to use it: calling preload() every time an item is added “can put significant strain on Shopify systems,” and rejected preloads fail silently, so the buyer just waits longer. Preload when intent is strong, such as when the cart screen opens.
Two more rules from the docs. A preloaded session reflects the cart at the moment you called preload, so call it again after the cart changes. And preload is a hint rather than a guarantee: during flash sales, a preload that would put the buyer in Shopify's queue is rejected.
import {useEffect} from 'react';
import {Button} from 'react-native';
import {useShopifyCheckoutSheet} from '@shopify/checkout-sheet-kit';
type Cart = {id: string; checkoutUrl: string; updatedAt: string};
function CartScreen({cart}: {cart: Cart}) {
const shopifyCheckout = useShopifyCheckoutSheet();
// The buyer is looking at their cart: strong intent, so preload here.
// updatedAt changes whenever the lines do, which re-runs the preload.
useEffect(() => {
shopifyCheckout.preload(cart.checkoutUrl);
}, [shopifyCheckout, cart.checkoutUrl, cart.updatedAt]);
return <Button title="Checkout" onPress={() => shopifyCheckout.present(cart.checkoutUrl)} />;
}4. Handle the lifecycle events
The kit emits completed, close, error, and pixel events, plus geolocationRequest on Android. Two cases matter most in production:
- A completed cart is spent. After
completed, the cart has become an order. If the app keeps the old cart ID, the next checkout fails. - Expired carts need a new cart, not a retry. The SDK raises
CheckoutExpiredErrorwhen the cart can no longer be checked out. Retrying the same URL will not help.
Note the type names. The README's event example types the error callback as CheckoutError, but the package exports CheckoutException. Every exception carries a code and a recoverable flag, and usually a message.
import {useEffect} from 'react';
import {
CheckoutExpiredError,
useShopifyCheckoutSheet,
type CheckoutCompletedEvent,
type CheckoutException,
} from '@shopify/checkout-sheet-kit';
function useCheckoutEvents({startNewCart}: {startNewCart: () => void}) {
const shopifyCheckout = useShopifyCheckoutSheet();
useEffect(() => {
const completed = shopifyCheckout.addEventListener('completed', (event: CheckoutCompletedEvent) => {
// The cart is now an order. Never reuse it.
startNewCart();
console.log('order', event.orderDetails.id);
});
const error = shopifyCheckout.addEventListener('error', (error: CheckoutException) => {
if (error instanceof CheckoutExpiredError) {
// This cart can no longer be checked out: start over with a fresh one.
startNewCart();
}
console.warn(error.message);
});
const close = shopifyCheckout.addEventListener('close', () => {
// The buyer backed out. The cart is still valid.
});
return () => {
completed?.remove();
error?.remove();
close?.remove();
};
}, [shopifyCheckout, startNewCart]);
}The pixel event relays Shopify's Web Pixel events from checkout to your app. Only forward them to analytics when you have the buyer's consent. Shopify puts that responsibility on the app, including Apple's App Tracking Transparency rules and GDPR.
Consent also has to reach checkout itself. Pass the buyer's choices through @inContext(visitorConsent: ...) on the cart mutation, with fields for analytics, preferences, marketing, and saleOfData. This needs Storefront API 2025-10 or later. Shopify encodes the choices into the returned checkoutUrl, so pass that URL to the kit unchanged. When the buyer changes their preferences, call shopifyCheckout.invalidate() and request a new URL, because a preloaded or older URL keeps the old choices (Shopify's privacy guide).
5. Don't skip the Android payment deep links
Some payment methods finish in a separate banking or wallet app. On Android 11 and later, your app can only open those apps if their URL schemes are declared in a <queries> block in the manifest. Without it, the deep link fails silently and the buyer is stuck. The README lists the schemes and includes an Expo config plugin that adds them for you. On iOS, set up universal links so the buyer returns to your app after paying elsewhere.
6. Signed-in buyers
Checkout is faster when it knows the buyer. There are several ways to tell it:
- Cart buyer identity. Set the email, address, and delivery preferences on the cart, and checkout opens pre-filled.
- Shop Pay. Set the cart's wallet preference to
shop_pay. The buyer signs in to Shop on their first checkout, and the app remembers it after that. - Multipass. For Shopify Plus stores on classic customer accounts. Build the Multipass token on your server, never in the app, with
return_toset to the checkout URL.
Stores on the newer customer accounts can also sign buyers in. The README says a bundled sign-in library is still in progress, but Shopify already documents the flow: authenticate the buyer against the Customer Account API with OAuth and PKCE, pass the access token as customerAccessToken in the cart's buyerIdentity, and present the checkoutUrl promptly, because authenticated checkout URLs expire (Shopify's guide).
Frequently asked questions
What is Shopify Checkout Kit?
Shopify's open-source SDK for presenting Shopify checkout inside a mobile app. Your app builds the cart with the Storefront API and hands the checkoutUrl to the kit. Shopify handles payments, taxes, and the order. There are Swift, Android, and React Native versions.
What is the React Native package?
@shopify/checkout-sheet-kit. Version 3.9.x supports React Native 0.76 and up on either architecture. 4.x requires the New Architecture.
Does it require Shopify Plus?
No. Checkout branding, Multipass, and UI extensions on the information, shipping, and payment steps require Plus. The kit itself does not.
Is Checkout Kit just a WebView?
It presents Shopify's web checkout, but in a native sheet the SDK manages, with preloading, lifecycle events, theming, and Shop Pay, Apple Pay, and checkout extension support. That is a lot more than a raw WebView pointed at the checkout URL.
Checkout is one piece of a custom Shopify app. If you are weighing a build against a subscription, start with our app builder price comparison, or see how we build custom iOS and Android apps for Shopify brands.