Version 2025-07 is the last API version to support React-based UI components. Later versions use Polaris web components, native UI elements with built-in accessibility, better performance, and consistent styling with Shopify's design system. Check out the upgrade guide to upgrade your extension and avoid being blocked from updating your extension after October 1st 2026.
Storage API
The Storage API lets you persist key-value data scoped to the customer across sessions. Use this API to store customer preferences, dismissed states, or other lightweight data that should survive navigation and return visits.
Anchor to Use casesUse cases
- Remember customer preferences: Save choices like preferred language, display options, or notification settings so they persist across visits.
- Track dismissed content: Store whether a customer has dismissed a promotional banner or onboarding message so it doesn't reappear.
- Cache lightweight data: Store small pieces of data to reduce redundant API calls, such as a customer's loyalty tier or last-viewed order.
Supported targets
- Customer
Account::Kitchen Sink - customer-account.
footer. render-after - customer-account.
order-index. announcement. render - customer-account.
order-index. block. render - customer-account.
order-status. announcement. render - customer-account.
order-status. block. render - customer-account.
order-status. cart-line-item. render-after - customer-account.
order-status. cart-line-list. render-after - customer-account.
order-status. customer-information. render-after - customer-account.
order-status. fulfillment-details. render-after - customer-account.
order-status. payment-details. render-after - customer-account.
order-status. return-details. render-after - customer-account.
order-status. unfulfilled-items. render-after - customer-account.
order. action. menu-item. render - customer-account.
order. action. render - customer-account.
order. page. render - customer-account.
page. render - customer-account.
profile. addresses. render-after - customer-account.
profile. announcement. render - customer-account.
profile. block. render - customer-account.
profile. company-details. render-after - customer-account.
profile. company-location-addresses. render-after - customer-account.
profile. company-location-payment. render-after - customer-account.
profile. company-location-staff. render-after - customer-account.
profile. payment. render-after
Supported targets
- Customer
Account::Kitchen Sink - customer-account.
footer. render-after - customer-account.
order-index. announcement. render - customer-account.
order-index. block. render - customer-account.
order-status. announcement. render - customer-account.
order-status. block. render - customer-account.
order-status. cart-line-item. render-after - customer-account.
order-status. cart-line-list. render-after - customer-account.
order-status. customer-information. render-after - customer-account.
order-status. fulfillment-details. render-after - customer-account.
order-status. payment-details. render-after - customer-account.
order-status. return-details. render-after - customer-account.
order-status. unfulfilled-items. render-after - customer-account.
order. action. menu-item. render - customer-account.
order. action. render - customer-account.
order. page. render - customer-account.
page. render - customer-account.
profile. addresses. render-after - customer-account.
profile. announcement. render - customer-account.
profile. block. render - customer-account.
profile. company-details. render-after - customer-account.
profile. company-location-addresses. render-after - customer-account.
profile. company-location-payment. render-after - customer-account.
profile. company-location-staff. render-after - customer-account.
profile. payment. render-after
Anchor to PropertiesProperties
The Storage API object provides key-value storage scoped to the customer. Access the following properties on the API object to read, write, and delete persistent data across sessions.
- Anchor to storagestoragestorageStorageStoragerequiredrequired
Key-value storage that persists across customer sessions. Data is scoped to your app and shared across all extension targets.
Storage
Key-value storage that persists across customer sessions. Data is scoped to your app and shared across all extension targets.
- delete
Deletes the stored data for the given key.
(key: string) => Promise<void> - read
Reads and returns a stored value by key. The stored data is deserialized from JSON and returned as its original type. Returns `null` if no data exists for the given key.
<T = unknown>(key: string) => Promise<T> - write
Writes data for the given key. The data must be serializable to JSON.
(key: string, data: any) => Promise<void>
Examples
Save and retrieve stored data
Description
Write a value to storage and read it back when the extension loads again. This example saves a locale preference using `shopify.storage.write()` and reads it when the extension loads with `shopify.storage.read()`.
React
import {useEffect, useState} from 'react'; import { reactExtension, useApi, BlockStack, Text, Button, } from '@shopify/ui-extensions-react/customer-account'; export default reactExtension( 'customer-account.order-status.block.render', () => <Extension />, ); function Extension() { const {storage} = useApi(); const [preference, setPreference] = useState<string | null>(null); useEffect(() => { async function loadPreference() { const stored = await storage.read('locale_pref'); if (stored) { setPreference(stored); } } loadPreference(); }, [storage]); async function handleSave(value: string) { await storage.write('locale_pref', value); setPreference(value); } return ( <BlockStack> <Text> {preference ? `Saved preference: ${preference}` : 'No preference saved'} </Text> <Button onPress={() => handleSave('en-US')}> Set preference to en-US </Button> </BlockStack> ); }TS
import { extension, BlockStack, Text, Button, } from '@shopify/ui-extensions/customer-account'; export default extension( 'customer-account.order-status.block.render', async (root, {storage}) => { const stored = await storage.read('locale_pref'); const text = root.createComponent( Text, {}, stored ? `Saved preference: ${stored}` : 'No preference saved', ); const button = root.createComponent( Button, { onPress: async () => { await storage.write( 'locale_pref', 'en-US', ); text.replaceChildren( 'Saved preference: en-US', ); }, }, 'Set preference to en-US', ); const stack = root.createComponent( BlockStack, {}, [text, button], ); root.appendChild(stack); }, );Delete stored data
Description
Remove a stored value when the customer resets a preference or state. This example tracks whether a promotion was dismissed and uses `shopify.storage.delete()` to clear the value when the customer wants to see it again.
React
import {useEffect, useState} from 'react'; import { reactExtension, useApi, BlockStack, Banner, Button, } from '@shopify/ui-extensions-react/customer-account'; export default reactExtension( 'customer-account.order-status.block.render', () => <Extension />, ); function Extension() { const {storage} = useApi(); const [dismissed, setDismissed] = useState(false); useEffect(() => { async function checkDismissed() { const value = await storage.read('promo_dismissed'); if (value === 'true') { setDismissed(true); } } checkDismissed(); }, [storage]); async function handleDismiss() { await storage.write( 'promo_dismissed', 'true', ); setDismissed(true); } async function handleReset() { await storage.delete('promo_dismissed'); setDismissed(false); } if (dismissed) { return ( <Button onPress={handleReset}> Show promotion again </Button> ); } return ( <BlockStack> <Banner title="Special offer!"> Get 20% off your next order. </Banner> <Button onPress={handleDismiss}> Dismiss </Button> </BlockStack> ); }TS
import { extension, BlockStack, Banner, Button, } from '@shopify/ui-extensions/customer-account'; export default extension( 'customer-account.order-status.block.render', async (root, {storage}) => { const value = await storage.read('promo_dismissed'); let dismissed = value === 'true'; const resetButton = root.createComponent( Button, { onPress: async () => { await storage.delete('promo_dismissed'); dismissed = false; render(); }, }, 'Show promotion again', ); const banner = root.createComponent( Banner, {title: 'Special offer!'}, 'Get 20% off your next order.', ); const dismissButton = root.createComponent( Button, { onPress: async () => { await storage.write( 'promo_dismissed', 'true', ); dismissed = true; render(); }, }, 'Dismiss', ); const promoStack = root.createComponent( BlockStack, {}, [banner, dismissButton], ); function render() { root.replaceChildren( dismissed ? resetButton : promoStack, ); } render(); }, );Track a dismissed banner
Description
Check storage on load to decide whether to show a promotional banner. When the customer dismisses it, write to storage so it stays hidden on future visits.
React
import {useEffect, useState} from 'react'; import { reactExtension, useApi, Banner, Text, } from '@shopify/ui-extensions-react/customer-account'; export default reactExtension( 'customer-account.order-status.block.render', () => <Extension />, ); function Extension() { const {storage} = useApi(); const [dismissed, setDismissed] = useState(true); useEffect(() => { async function checkDismissed() { const value = await storage.read('promo_dismissed'); setDismissed(value === 'true'); } checkDismissed(); }, [storage]); async function handleDismiss() { await storage.write( 'promo_dismissed', 'true', ); setDismissed(true); } if (dismissed) { return null; } return ( <Banner title="Free shipping on your next order" onDismiss={handleDismiss} > <Text> Use code FREESHIP at checkout. </Text> </Banner> ); }TS
import { extension, Banner, Text, } from '@shopify/ui-extensions/customer-account'; export default extension( 'customer-account.order-status.block.render', async (root, {storage}) => { const value = await storage.read('promo_dismissed'); if (value === 'true') { return; } const text = root.createComponent( Text, {}, 'Use code FREESHIP at checkout.', ); const banner = root.createComponent( Banner, { title: 'Free shipping on your next order', onDismiss: async () => { await storage.write( 'promo_dismissed', 'true', ); banner.remove(); }, }, [text], ); root.appendChild(banner); }, );
Anchor to Best practicesBest practices
- Store only small values: Keep stored data lightweight. Storage is intended for simple key-value pairs like preferences and flags, not large datasets.
- Use descriptive keys: Name your storage keys clearly (for example,
promo_dismissedorlocale_pref) so their purpose is obvious and conflicts with other extensions are unlikely. - Handle missing values gracefully: Always check for
nullorundefinedwhen reading from storage, since the value may not exist on the customer's first visit. - Don't store sensitive data: Storage isn't encrypted. Don't store personal information, tokens, or anything that could compromise customer privacy.
Anchor to LimitationsLimitations
- Storage is scoped per customer and per extension. You can't share data between different extensions or access another customer's stored values.
- On the pre-authenticated Order status page, storage is scoped to the customer associated with the order, not the browsing session.
- All values are stored as strings. You must serialize and deserialize complex types (like JSON objects).