--- title: POS UI extensions description: The UI Extensions library enables individuals to build extensions that use interface elements and behaviors that mirror the look and feel of the POS retail experience. These elements render natively, providing the performance and accessibility inherent to a native app. api_version: 2025-10 api_name: pos-ui-extensions source_url: html: https://shopify.dev/docs/api/pos-ui-extensions?itcat=partner_blog md: https://shopify.dev/docs/api/pos-ui-extensions.md?itcat=partner_blog --- # POS UI extensions The UI Extensions library enables individuals to build extensions that use interface elements and behaviors that mirror the look and feel of the POS retail experience. These elements render natively, providing the performance and accessibility inherent to a native app. ## API versioning POS UI extensions are built on a versioned API that receives regular updates with new features, improvements, and additional targets. We recommend using the latest supported API version to access the most current capabilities and ensure optimal compatibility with POS devices. You can track new releases and update your extensions by referencing the [developer changelog](https://shopify.dev/changelog). ## Overview Extend the Shopify POS with UI Extensions. [![](https://shopify.dev/images/icons/32/pickaxe-1.png)![](https://shopify.dev/images/icons/32/pickaxe-1-dark.png)](https://shopify.dev/docs/api/pos-ui-extensions/targets) [Extension targetsSee all available extension targets](https://shopify.dev/docs/api/pos-ui-extensions/targets) [![](https://shopify.dev/images/icons/32/pickaxe-2.png)![](https://shopify.dev/images/icons/32/pickaxe-2-dark.png)](https://shopify.dev/docs/api/pos-ui-extensions/apis) [APIsSee all available APIs](https://shopify.dev/docs/api/pos-ui-extensions/apis) [![](https://shopify.dev/images/icons/32/blocks.png)![](https://shopify.dev/images/icons/32/blocks-dark.png)](https://shopify.dev/docs/api/pos-ui-extensions/polaris-web-components) [ComponentsSee all available components](https://shopify.dev/docs/api/pos-ui-extensions/polaris-web-components) ## Getting Started Get started with POS UI Extensions with Shopify CLI. [![](https://shopify.dev/images/icons/32/blocks.png)![](https://shopify.dev/images/icons/32/blocks-dark.png)](https://shopify.dev/docs/api/pos-ui-extensions/getting-started) [Set up your development environmentGetting started guide](https://shopify.dev/docs/api/pos-ui-extensions/getting-started) ## App Authentication POS UI extensions can also make authenticated calls to your app's backend. When you use `fetch()` to make a request to your app's configured auth domain or any of its subdomains, an `Authorization` header is automatically added with a Shopify [OpenID Connect ID Token (formerly known as a Session Token)](https://shopify.dev/docs/api/app-bridge-library/reference/id-token). There's no need to manually manage ID tokens. Relative URLs passed to `fetch()` are resolved against your app's `app_url`. This means if your app's backend is on the same domain as your `app_url`, you can make requests to it using `fetch('/path')`. If you need to make requests to a different domain, you can use the [`session.getSessionToken()` method](apis/session-api#sessionapi-propertydetail-getsessiontoken) to retrieve the ID token and manually add it to your request headers. **Important**: ID tokens are only returned for authenticated users who are permitted to use your app. When the authenticated user (the user that logged into Shopify POS with their email address) doesn't have the correct app permission enabled for your app, the token will be null. This is irrelevant of which POS Staff member is pinned in, as those are not authenticated users. For more information on configuring app permissions, see the [Shopify app permissions documentation](https://help.shopify.com/en/manual/your-account/users/roles/permissions/store-permissions#apps-and-channels-permissions). ### Examples * #### Make requests to your app's backend ##### JSX ```jsx import {render} from 'preact'; import {useState, useEffect} from 'preact/hooks'; export default async () => { render(, document.body); }; export function CustomerDetailsBlock() { const [loyaltyInfo, setLoyaltyInfo] = useState(''); useEffect(() => { getLoyaltyInfo(); }, [shopify.customer.id]); async function getLoyaltyInfo() { console.log('fetching', `${URL}/api/loyalty/${shopify.customer.id}`) const res = await fetch(`${URL}/api/loyalty/${shopify.customer.id}`); const json = await res.json(); setLoyaltyInfo(json.loyaltySummary); } return ( {loyaltyInfo} ); } ``` ## Direct API access You can make Shopify Admin API requests directly from your extension using the standard [web fetch API](https://developer.mozilla.org/en-US/docs/Web/API/fetch)! Any `fetch()` calls from your extension to Shopify's Admin GraphQL API are automatically authenticated by default. These calls are fast too, because Shopify handles requests directly. Direct API requests use [online access](https://shopify.dev/docs/apps/build/authentication-authorization/access-token-types/online-access-tokens) mode by default. Access scopes Be sure to declare all required access scopes in your app's TOML file. For local development, access scopes are only registered or updated when the app is deployed and installed on your test store. [![](https://shopify.dev/images/icons/32/information.png)![](https://shopify.dev/images/icons/32/information-dark.png)](https://shopify.dev/docs/api/usage/access-scopes) [Developer guideLearn more about access scopes](https://shopify.dev/docs/api/usage/access-scopes) ### Examples * #### Query Shopify data directly ##### JSX ```jsx import {render} from 'preact'; import {useState, useEffect} from 'preact/hooks'; // This mutation requires the `write_products` access scope. // /docs/api/admin-graphql/latest/mutations/metafieldsset async function mutateMetafield(productId) { const requestBody = { query: `#graphql mutation MetafieldsSet($metafields: [MetafieldsSetInput!]!) { metafieldsSet(metafields: $metafields) { metafields { key namespace value createdAt updatedAt } } } `, variables: { metafields: [ { key: 'direct_api', namespace: 'custom', ownerId: `gid://shopify/Product/${productId}`, value: 'Example Value', type: 'single_line_text_field', }, ], }, }; await fetch('shopify:admin/api/graphql.json', { method: 'POST', body: JSON.stringify(requestBody), }); } // This query requires the `read_products` access scope. // /docs/api/admin-graphql/latest/queries/product async function queryProductMetafields(productId) { const requestBody = { query: `#graphql query GetProduct($id: ID!) { product(id: $id) { id metafields(first: 10) { edges { node { id namespace key value } } } } } `, variables: {id: `gid://shopify/Product/${productId}`}, }; const res = await fetch('shopify:admin/api/graphql.json', { method: 'POST', body: JSON.stringify(requestBody), }); return res.json(); } export default async () => { render(, document.body); }; export function ProductDetailsBlock() { const [productInfo, setProductInfo] = useState(''); useEffect(() => { async function getProductInfo() { const result = await queryProductMetafields(shopify.product.id); setProductInfo(JSON.stringify(result, null, 2)); } getProductInfo(); }, [shopify.product.id]); return ( Metafields: {productInfo} Set Metafields: {productInfo} mutateMetafield(shopify.product.id)}>Set Metafields ); } ```