shopify-pos
npx skills add https://github.com/toilahuongg/shopify-agents-kit --skill shopify-pos
Agent 安装分布
Skill 文档
Shopify POS UI Extensions (2026)
Build custom extensions that integrate directly into Shopify’s Point of Sale interface on iOS and Android devices.
Official References
Prerequisites
- Shopify CLI (latest)
- Shopify App with POS enabled
- Development store with POS Pro subscription
Enable POS embedding: In Partner Dashboard > App > Configuration, set “Embed app in Shopify POS” to True.
Extension Architecture
POS UI extensions have three interconnected parts:
- Targets – Where your extension appears (tile, modal, block, menu item)
- Target APIs – Data and functionality access (Cart, Customer, Session, etc.)
- Components – Native UI building blocks (Button, Screen, List, etc.)
Creating a POS Extension
shopify app generate extension --template pos_ui --name "my-pos-extension"
Configuration (shopify.extension.toml)
api_version = "2025-10"
[[extensions]]
type = "ui_extension"
name = "my-pos-extension"
handle = "my-pos-extension"
[[extensions.targeting]]
module = "./src/Tile.tsx"
target = "pos.home.tile.render"
[[extensions.targeting]]
module = "./src/Modal.tsx"
target = "pos.home.modal.render"
Targets Reference
See references/targets.md for all available targets.
Target Types
| Type | Purpose | Example |
|---|---|---|
| Tile | Smart grid button on home screen | pos.home.tile.render |
| Modal | Full-screen interface | pos.home.modal.render |
| Block | Inline content section | pos.product-details.block.render |
| Menu Item | Action menu button | pos.customer-details.action.menu-item.render |
Common Target Patterns
Home Screen (Smart Grid)
// Tile.tsx - Entry point on POS home
import { Tile, reactExtension } from '@shopify/ui-extensions-react/point-of-sale';
export default reactExtension('pos.home.tile.render', () => <TileComponent />);
function TileComponent() {
return <Tile title="My App" subtitle="Tap to open" enabled={true} />;
}
Modal (Full Screen)
// Modal.tsx - Launches when tile is tapped
import { Screen, Navigator, Text, Button, useApi, reactExtension } from '@shopify/ui-extensions-react/point-of-sale';
export default reactExtension('pos.home.modal.render', () => <ModalComponent />);
function ModalComponent() {
const api = useApi<'pos.home.modal.render'>();
return (
<Navigator>
<Screen name="Main" title="My Extension">
<Text>Welcome to my POS extension</Text>
<Button title="Close" onPress={() => api.navigation.dismiss()} />
</Screen>
</Navigator>
);
}
Block (Inline Content)
// ProductBlock.tsx
import { Section, Text, reactExtension, useApi } from '@shopify/ui-extensions-react/point-of-sale';
export default reactExtension('pos.product-details.block.render', () => <ProductBlock />);
function ProductBlock() {
const { product } = useApi<'pos.product-details.block.render'>();
const productData = product.getProduct();
return (
<Section title="Custom Info">
<Text>Product ID: {productData?.id}</Text>
</Section>
);
}
Components Reference
See references/components.md for all available components.
Key Components
Layout & Structure
Screen– Navigation screen with title, loading state, actionsNavigator– Screen navigation containerScrollView– Scrollable content containerSection– Card-like grouping containerStack– Horizontal/vertical layoutList– Structured data rows
Actions
Button– Tappable action buttonTile– Smart grid tile (home screen only)Selectable– Make components tappable
Forms
TextField,TextArea– Text inputNumberField– Numeric inputEmailField– Email with validationDateField,DatePicker– Date selectionRadioButtonList– Single selectionStepper– Increment/decrement controlPinPad– Secure PIN entry
Feedback
Banner– Important messagesDialog– Confirmation promptsBadge– Status indicators
Media
Icon– POS icon catalogImage– Visual contentCameraScanner– Barcode/QR scanning
APIs Reference
See references/apis.md for all available APIs.
Accessing APIs
import { useApi } from '@shopify/ui-extensions-react/point-of-sale';
function MyComponent() {
const api = useApi<'pos.home.modal.render'>();
// Access various APIs based on target
const { cart, customer, session, navigation, toast } = api;
}
Core APIs
Cart API – Modify cart contents
const { cart } = useApi<'pos.home.modal.render'>();
// Add item
await cart.addLineItem({ variantId: 'gid://shopify/ProductVariant/123', quantity: 1 });
// Apply discount
await cart.applyCartDiscount({ type: 'percentage', value: 10, title: '10% Off' });
// Get cart
const currentCart = cart.getCart();
Session API – Authentication and session data
const { session } = useApi<'pos.home.modal.render'>();
// Get session token for backend auth
const token = await session.getSessionToken();
// Get current staff member
const staff = session.currentSession;
Customer API – Customer data access
const { customer } = useApi<'pos.customer-details.block.render'>();
const customerData = customer.getCustomer();
Toast API – Show notifications
const { toast } = useApi<'pos.home.modal.render'>();
toast.show('Item added successfully');
Navigation API – Screen navigation
const { navigation } = useApi<'pos.home.modal.render'>();
navigation.dismiss(); // Close modal
navigation.navigate('ScreenName'); // Navigate to screen
Scanner API – Barcode scanning
const { scanner } = useApi<'pos.home.modal.render'>();
const result = await scanner.scanBarcode();
Print API – Receipt printing
const { print } = useApi<'pos.home.modal.render'>();
await print.printDocument(documentContent);
Direct GraphQL API Access
Available for extensions targeting 2025-07 or later (requires POS 10.6.0+).
const response = await fetch('shopify:admin/api/graphql.json', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `
query GetProduct($id: ID!) {
product(id: $id) {
title
variants(first: 10) {
nodes { id title inventoryQuantity }
}
}
}
`,
variables: { id: 'gid://shopify/Product/123' }
})
});
Declare required scopes in shopify.app.toml:
[access_scopes]
scopes = "read_products,write_products,read_customers"
Development Workflow
Local Development
shopify app dev
Open the Shopify POS app on your device and connect to the development store.
Testing
- Install app on development store
- Open Shopify POS app
- Navigate to smart grid (home) to see tiles
- Tap tiles to test modals
- Navigate to relevant screens (products, customers, orders) for block/action targets
Deployment
shopify app deploy
Best Practices
- Performance First – Extensions run in critical merchant workflows; minimize API calls and computations
- Offline Consideration – Use Storage API for data that should persist offline
- Native Feel – Use provided components to match POS design system
- Error Handling – Always handle API failures gracefully with user feedback
- Loading States – Show loading indicators during async operations
Storage API for Offline Data
const { storage } = useApi<'pos.home.modal.render'>();
// Store data
await storage.setItem('key', JSON.stringify(data));
// Retrieve data
const stored = await storage.getItem('key');
const data = stored ? JSON.parse(stored) : null;
Complete Example: Loyalty Points Extension
// Tile.tsx
import { Tile, reactExtension } from '@shopify/ui-extensions-react/point-of-sale';
export default reactExtension('pos.home.tile.render', () => (
<Tile title="Loyalty Points" subtitle="Check & redeem" enabled={true} />
));
// Modal.tsx
import {
Screen, Navigator, Text, Button, Section, Stack,
useApi, reactExtension
} from '@shopify/ui-extensions-react/point-of-sale';
import { useState, useEffect } from 'react';
export default reactExtension('pos.home.modal.render', () => <LoyaltyModal />);
function LoyaltyModal() {
const { cart, session, navigation, toast } = useApi<'pos.home.modal.render'>();
const [points, setPoints] = useState(0);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchPoints();
}, []);
async function fetchPoints() {
const token = await session.getSessionToken();
const currentCart = cart.getCart();
const customerId = currentCart?.customer?.id;
if (!customerId) {
setLoading(false);
return;
}
const res = await fetch('https://your-backend.com/api/points', {
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify({ customerId })
});
const data = await res.json();
setPoints(data.points);
setLoading(false);
}
async function redeemPoints() {
await cart.applyCartDiscount({
type: 'fixedAmount',
value: points / 100,
title: 'Loyalty Redemption'
});
toast.show('Points redeemed!');
navigation.dismiss();
}
return (
<Navigator>
<Screen name="Main" title="Loyalty Points" isLoading={loading}>
<Section title="Current Balance">
<Stack direction="vertical" spacing={2}>
<Text variant="headingLarge">{points} points</Text>
<Text>Worth ${(points / 100).toFixed(2)}</Text>
</Stack>
</Section>
<Button
title="Redeem All Points"
type="primary"
onPress={redeemPoints}
disabled={points === 0}
/>
<Button title="Close" onPress={() => navigation.dismiss()} />
</Screen>
</Navigator>
);
}