Loading...
Loading...
Compare original and translation side by side
shopify app generate extension --template pos_ui --name "my-pos-extension"shopify app generate extension --template pos_ui --name "my-pos-extension"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"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"| Type | Purpose | Example |
|---|---|---|
| Tile | Smart grid button on home screen | |
| Modal | Full-screen interface | |
| Block | Inline content section | |
| Menu Item | Action menu button | |
| 类型 | 用途 | 示例 |
|---|---|---|
| 磁贴(Tile) | 首页智能网格按钮 | |
| 模态框(Modal) | 全屏界面 | |
| 区块(Block) | 行内内容区域 | |
| 菜单项(Menu Item) | 操作菜单按钮 | |
// 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.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>
);
}// 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>
);
}// 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.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>
);
}// 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>
);
}ScreenNavigatorScrollViewSectionStackListButtonTileSelectableTextFieldTextAreaNumberFieldEmailFieldDateFieldDatePickerRadioButtonListStepperPinPadBannerDialogBadgeIconImageCameraScannerScreenNavigatorScrollViewSectionStackListButtonTileSelectableTextFieldTextAreaNumberFieldEmailFieldDateFieldDatePickerRadioButtonListStepperPinPadBannerDialogBadgeIconImageCameraScannerimport { 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;
}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;
}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();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;const { customer } = useApi<'pos.customer-details.block.render'>();
const customerData = customer.getCustomer();const { toast } = useApi<'pos.home.modal.render'>();
toast.show('Item added successfully');const { navigation } = useApi<'pos.home.modal.render'>();
navigation.dismiss(); // Close modal
navigation.navigate('ScreenName'); // Navigate to screenconst { scanner } = useApi<'pos.home.modal.render'>();
const result = await scanner.scanBarcode();const { print } = useApi<'pos.home.modal.render'>();
await print.printDocument(documentContent);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();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;const { customer } = useApi<'pos.customer-details.block.render'>();
const customerData = customer.getCustomer();const { toast } = useApi<'pos.home.modal.render'>();
toast.show('Item added successfully');const { navigation } = useApi<'pos.home.modal.render'>();
navigation.dismiss(); // Close modal
navigation.navigate('ScreenName'); // Navigate to screenconst { scanner } = useApi<'pos.home.modal.render'>();
const result = await scanner.scanBarcode();const { print } = useApi<'pos.home.modal.render'>();
await print.printDocument(documentContent);2025-07const 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' }
})
});shopify.app.toml[access_scopes]
scopes = "read_products,write_products,read_customers"2025-07const 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' }
})
});shopify.app.toml[access_scopes]
scopes = "read_products,write_products,read_customers"shopify app devshopify app devshopify app deployshopify app deployconst { 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;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;// 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>
);
}// 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>
);
}