Build merchant admin interfaces with Shopify Polaris—a React-based design system optimized for eCommerce efficiency, data richness, and action-driven workflows. Trigger this skill when designing admin dashboards, product tables, order management UIs, or responsive merchant-facing tools.
Polaris is Shopify's design system built for merchant admin interfaces. It prioritizes merchant efficiency and data richness—helping sellers accomplish complex tasks quickly. Every component is WCAG compliant and responsive by default. Polaris uses React as its foundation, with semantic HTML and accessible patterns built-in.
1. Efficiency Over Aesthetics Merchants need to complete tasks fast. Polaris favors scannable layouts, clear action buttons, and minimal friction. Data tables expose many rows; modals open quickly.
2. Action-Driven Every interface guides users toward primary actions. Primary buttons are prominent; secondary actions recede. Forms and tables highlight next steps.
3. Clarity Through Constraint A restrained color palette (primarily navy, teal, and red for actions) reduces cognitive load. Consistent spacing (4px, 8px, 12px, 16px grids) creates predictable rhythm.
4. Mobile-First Responsive Polaris components adapt seamlessly to small screens. Stacked layouts, touch-friendly targets (48px minimum), hidden overflow patterns.
Colors:
Typography:
Spacing:
Elevation & Shadows:
0 1px 3px rgba(0,0,0,0.1)0 5px 15px rgba(0,0,0,0.15) (stronger focus)A flexible list for showing items (products, orders). Renders rows with thumbnail, title, metadata.
<ResourceList
resourceName={{ singular: 'product', plural: 'products' }}
items={products}
renderItem={(item) => (
<ResourceItem
id={item.id}
media={<Thumbnail source={item.image} />}
accessibilityLabel={item.title}
>
<Text variant="bodyMd" fontWeight="bold">{item.title}</Text>
<Text variant="bodySm" color="subdued">${item.price}</Text>
</ResourceItem>
)}
/>
Data table for large, sortable datasets. Supports row selection, sticky headers.
<IndexTable
selectable={true}
headings={[
{ title: 'Order' },
{ title: 'Date' },
{ title: 'Customer', alignment: 'start' },
{ title: 'Total', alignment: 'end' }
]}
rows={orders.map(order => ({
id: order.id,
cells: [order.number, order.date, order.customer, `$${order.total}`]
}))}
/>
Page is the top container; Card groups content.
<Page
title="Products"
primaryAction={{ content: 'Add product', onAction: () => {} }}
secondaryActions={[{ content: 'Export' }]}
>
<Layout>
<Layout.Section>
<Card title="Inventory">
<Box padding="400">{/* content */}</Box>
</Card>
</Layout.Section>
</Layout>
</Page>
Use Polaris when:
React patterns:
@shopify/polaris: Button, Card, Page, IndexTable, ResourceList, Form, TextField, Select, Modal, Banner<PolarisProvider> for themingStack for layout (direction: "vertical" or "horizontal")Box for spacing with padding/margin tokensCSS (if extending):
padding: 16px 20px, gap: 16pxfont-size: 14px; line-height: 20px; color: #2121211px solid #E8E8E8background: #0080FF; color: white; padding: 10px 16px; border-radius: 4pxaria-label or associated <label><button>, <input>, <table>, not <div> + click handlersInput: "Create a product list UI with a bulk delete button for merchants"
import { Page, ResourceList, ResourceItem, Button, Box } from '@shopify/polaris';
export default function ProductListPage() {
const [selected, setSelected] = useState([]);
return (
<Page
title="Products"
primaryAction={{ content: 'Add product' }}
>
<Box marginBlockEnd="400">
{selected.length > 0 && (
<Button destructive onClick={() => deleteProducts(selected)}>
Delete {selected.length} products
</Button>
)}
</Box>
<ResourceList
resourceName={{ singular: 'product', plural: 'products' }}
items={products}
selectable={true}
selectedItems={selected}
onSelectionChange={setSelected}
renderItem={(item) => (
<ResourceItem id={item.id} selected={selected.includes(item.id)}>
<Text fontWeight="bold">{item.title}</Text>
</ResourceItem>
)}
/>
</Page>
);
}
Input: "Design an order detail page showing line items in a table with status badges"
import { Page, Card, IndexTable, Badge, Text } from '@shopify/polaris';
export default function OrderDetail({ orderId }) {
const order = fetchOrder(orderId);
return (
<Page title={`Order ${order.number}`} backAction={{ onAction: () => {} }}>
<Card title="Line items">
<IndexTable
headings={[
{ title: 'Product' },
{ title: 'SKU' },
{ title: 'Qty' },
{ title: 'Status', alignment: 'center' },
{ title: 'Price', alignment: 'end' }
]}
rows={order.items.map(item => ({
id: item.id,
cells: [
item.title,
item.sku,
item.quantity,
<Badge status={item.status}>{item.status}</Badge>,
`$${item.price}`
]
}))}
/>
</Card>
</Page>
);
}