Index
Most apps need a way for merchants to manage collections of resources and perform actions on them. For React Router-based Shopify apps, that index route file (app.[resources].jsx) should use a plural noun for the resource type. For example, app.products.jsx for managing products through your app.
The index pattern provides this foundation while following proven design guidelines that help your app feel native to the Shopify admin. See Built for Shopify requirements for more details on these guidelines.
Anchor to ExamplesExamples
Anchor to Display an index table with filters, search, and bulk actionsDisplay an index table with filters, search, and bulk actions
Merchants need to manage a collection of resources and perform actions on them. This example displays an index table for a Product Quality Auditor app with filter tabs, search, sortable columns, bulk selection, and bulk actions. The table shows product thumbnails, quality scores, issue counts, and status badges.
Preview
jsx
<s-page heading="Puzzles">
<s-button slot="primary-action" variant="primary">
Create puzzle
</s-button>
<s-button slot="secondary-actions" variant="secondary">
Export puzzles
</s-button>
<s-button slot="secondary-actions" variant="secondary">
Import puzzles
</s-button>
{/* === */}
{/* Empty state */}
{/* This should only be visible if the merchant has not created any puzzles yet. */}
{/* === */}
<s-section accessibilityLabel="Empty state section">
<s-grid gap="base" justifyItems="center" paddingBlock="large-400">
<s-box maxInlineSize="200px" maxBlockSize="200px">
{/* aspectRatio should match the actual image dimensions (width/height) */}
<s-image
aspectRatio="1/0.5"
src="https://cdn.shopify.com/static/images/polaris/patterns/callout.png"
alt="A stylized graphic of four characters, each holding a puzzle piece"
/>
</s-box>
<s-grid justifyItems="center" maxInlineSize="450px" gap="base">
<s-stack alignItems="center">
<s-heading>Start creating puzzles</s-heading>
<s-paragraph>
Create and manage your collection of puzzles for players to
enjoy.
</s-paragraph>
</s-stack>
<s-button-group>
<s-button
slot="secondary-actions"
accessibilityLabel="Learn more about creating puzzles"
>
{" "}
Learn more{" "}
</s-button>
<s-button slot="primary-action" accessibilityLabel="Add a new puzzle">
{" "}
Create puzzle{" "}
</s-button>
</s-button-group>
</s-grid>
</s-grid>
</s-section>
{/* === */}
{/* Table */}
{/* This should only be visible if the merchant has created one or more puzzles. */}
{/* === */}
<s-section padding="none" accessibilityLabel="Puzzles table section">
<s-table>
<s-table-header-row>
<s-table-header listSlot="primary">Puzzle</s-table-header>
<s-table-header format="numeric">Pieces</s-table-header>
<s-table-header>Created</s-table-header>
<s-table-header>Status</s-table-header>
</s-table-header-row>
<s-table-body>
<s-table-row>
<s-table-cell>
<s-stack direction="inline" gap="small" alignItems="center">
<s-clickable
href="#"
accessibilityLabel="Mountain View puzzle thumbnail"
border="base"
borderRadius="base"
overflow="hidden"
inlineSize="40px"
blockSize="40px"
>
<s-image
objectFit="cover"
alt="Mountain View puzzle thumbnail"
src="https://picsum.photos/id/29/80/80"
/>
</s-clickable>
<s-link href="#">Mountain View</s-link>
</s-stack>
</s-table-cell>
<s-table-cell>16</s-table-cell>
<s-table-cell>Today</s-table-cell>
<s-table-cell>
<s-badge color="base" tone="success">
Active
</s-badge>
</s-table-cell>
</s-table-row>
<s-table-row>
<s-table-cell>
<s-stack direction="inline" gap="small" alignItems="center">
<s-clickable
href="#"
accessibilityLabel="Ocean Sunset puzzle thumbnail"
border="base"
borderRadius="base"
overflow="hidden"
inlineSize="40px"
blockSize="40px"
>
<s-image
objectFit="cover"
alt="Ocean Sunset puzzle thumbnail"
src="https://picsum.photos/id/12/80/80"
/>
</s-clickable>
<s-link href="#">Ocean Sunset</s-link>
</s-stack>
</s-table-cell>
<s-table-cell>9</s-table-cell>
<s-table-cell>Yesterday</s-table-cell>
<s-table-cell>
<s-badge color="base" tone="success">
Active
</s-badge>
</s-table-cell>
</s-table-row>
<s-table-row>
<s-table-cell>
<s-stack direction="inline" gap="small" alignItems="center">
<s-clickable
href="#"
accessibilityLabel="Forest Animals puzzle thumbnail"
border="base"
borderRadius="base"
overflow="hidden"
inlineSize="40px"
blockSize="40px"
>
<s-image
objectFit="cover"
alt="Forest Animals puzzle thumbnail"
src="https://picsum.photos/id/324/80/80"
/>
</s-clickable>
<s-link href="#">Forest Animals</s-link>
</s-stack>
</s-table-cell>
<s-table-cell>25</s-table-cell>
<s-table-cell>Last week</s-table-cell>
<s-table-cell>
<s-badge color="base" tone="neutral">
Draft
</s-badge>
</s-table-cell>
</s-table-row>
{/* Add more rows as needed here */}
{/* If more than 100 rows are needed, index page tables should use the paginate, hasPreviousPage, hasNextPage, onPreviousPage, and onNextPage attributes to display and handle pagination) */}
</s-table-body>
</s-table>
</s-section>
{/* Footer help */}
<s-stack alignItems="center" paddingBlock="large">
<s-text color="subdued">
Learn more about <s-link href="https://help.shopify.com" target="_blank">improving product quality</s-link>.
</s-text>
</s-stack>
</s-page>html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.shopify.com/shopifycloud/polaris.js"></script>
<title>Pattern</title>
</head>
<body>
<!-- === -->
<!-- Index page pattern -->
<!-- === -->
<s-page heading="Puzzles">
<s-button slot="primary-action" variant="primary">Create puzzle</s-button>
<s-button slot="secondary-actions" variant="secondary">Export puzzles</s-button>
<s-button slot="secondary-actions" variant="secondary">Import puzzles</s-button>
<!-- === -->
<!-- Empty state -->
<!-- This should only be visible if the merchant has not created any puzzles yet. -->
<!-- === -->
<s-section accessibilityLabel="Empty state section">
<s-grid gap="base" justifyItems="center" paddingBlock="large-400">
<s-box maxInlineSize="200px" maxBlockSize="200px">
<!-- aspectRatio should match the actual image dimensions (width/height) -->
<s-image
aspectRatio="1/0.5"
src="https://cdn.shopify.com/static/images/polaris/patterns/callout.png"
alt="A stylized graphic of four characters, each holding a puzzle piece"
/>
</s-box>
<s-grid justifyItems="center" maxInlineSize="450px" gap="base">
<s-stack alignItems="center">
<s-heading>Start creating puzzles</s-heading>
<s-paragraph>
Create and manage your collection of puzzles for players to enjoy.
</s-paragraph>
</s-stack>
<s-button-group>
<s-button slot="secondary-actions" aria-label="Learn more about creating puzzles"> Learn more </s-button>
<s-button slot="primary-action" aria-label="Add a new puzzle"> Create puzzle </s-button>
</s-button-group>
</s-grid>
</s-grid>
</s-section>
<!-- === -->
<!-- Table -->
<!-- This should only be visible if the merchant has created one or more puzzles. -->
<!-- === -->
<s-section padding="none" accessibilityLabel="Puzzles table section">
<s-table>
<s-table-header-row>
<s-table-header listSlot="primary">Puzzle</s-table-header>
<s-table-header format="numeric">Pieces</s-table-header>
<s-table-header>Created</s-table-header>
<s-table-header>Status</s-table-header>
</s-table-header-row>
<s-table-body>
<s-table-row>
<s-table-cell>
<s-stack direction="inline" gap="small" alignItems="center">
<s-clickable
href="#"
accessibilityLabel="Mountain View puzzle thumbnail"
border="base"
borderRadius="base"
overflow="hidden"
inlineSize="40px"
blockSize="40px"
>
<s-image objectFit="cover" alt="Mountain View puzzle thumbnail" src="https://picsum.photos/id/29/80/80"></s-image>
</s-clickable>
<s-link href="#">Mountain View</s-link>
</s-stack>
</s-table-cell>
<s-table-cell>16</s-table-cell>
<s-table-cell>Today</s-table-cell>
<s-table-cell>
<s-badge color="base" tone="success"> Active </s-badge>
</s-table-cell>
</s-table-row>
<s-table-row>
<s-table-cell>
<s-stack direction="inline" gap="small" alignItems="center">
<s-clickable
href="#"
accessibilityLabel="Ocean Sunset puzzle thumbnail"
border="base"
borderRadius="base"
overflow="hidden"
inlineSize="40px"
blockSize="40px"
>
<s-image objectFit="cover" alt="Ocean Sunset puzzle thumbnail" src="https://picsum.photos/id/12/80/80"></s-image>
</s-clickable>
<s-link href="#">Ocean Sunset</s-link>
</s-stack>
</s-table-cell>
<s-table-cell>9</s-table-cell>
<s-table-cell>Yesterday</s-table-cell>
<s-table-cell>
<s-badge color="base" tone="success"> Active </s-badge>
</s-table-cell>
</s-table-row>
<s-table-row>
<s-table-cell>
<s-stack direction="inline" gap="small" alignItems="center">
<s-clickable
href="#"
accessibilityLabel="Forest Animals puzzle thumbnail"
border="base"
borderRadius="base"
overflow="hidden"
inlineSize="40px"
blockSize="40px"
>
<s-image objectFit="cover" alt="Forest Animals puzzle thumbnail" src="https://picsum.photos/id/324/80/80"></s-image>
</s-clickable>
<s-link href="#">Forest Animals</s-link>
</s-stack>
</s-table-cell>
<s-table-cell>25</s-table-cell>
<s-table-cell>Last week</s-table-cell>
<s-table-cell>
<s-badge color="base" tone="neutral"> Draft </s-badge>
</s-table-cell>
</s-table-row>
<!-- Add more rows as needed here -->
<!-- If more than 100 rows are needed, index page tables should use the paginate, hasPreviousPage, hasNextPage, onPreviousPage, and onNextPage attributes to display and handle pagination) -->
</s-table-body>
</s-table>
</s-section>
<!-- Footer help -->
<s-stack alignItems="center" paddingBlock="large">
<s-text color="subdued">Learn more about <s-link href="https://help.shopify.com" target="_blank">improving product quality</s-link>.</s-text>
</s-stack>
</s-page>
</body>
</html>Anchor to Make empty states actionable using IntentsMake empty states actionable using Intents
Use the Intents API to open native Shopify interfaces for creating products directly from an empty state.
Preview
jsx
{/* @validate-ignore: possibly 'undefined', Cannot invoke an object */}
<s-section accessibilityLabel="Empty state with intents">
<s-grid gap="base" justifyItems="center" paddingBlock="large-400">
<s-box maxInlineSize="200px" maxBlockSize="200px">
<s-image
aspectRatio="1/0.5"
src="https://cdn.shopify.com/static/images/polaris/patterns/callout.png"
alt="Illustration showing product creation"
/>
</s-box>
<s-grid justifyItems="center" maxInlineSize="450px" gap="base">
<s-stack alignItems="center">
<s-heading>No products yet</s-heading>
<s-paragraph>
Add products from your Shopify catalog to get started with quality auditing.
</s-paragraph>
</s-stack>
<s-button-group>
<s-button
slot="secondary-actions"
onClick={() => {
shopify.intents.invoke('browse:shopify/Product');
}}
>
Browse products
</s-button>
<s-button
slot="primary-action"
onClick={async () => {
const activity = await shopify.intents.invoke('create:shopify/Product');
const response = await activity.complete;
if (response.code === 'ok') {
shopify.toast.show('Product created');
}
}}
>
Create product
</s-button>
</s-button-group>
</s-grid>
</s-grid>
</s-section>html
<!-- Empty state with Intents API actions -->
<!-- Note: Intents require JavaScript to invoke. This HTML shows the UI structure. -->
<s-section accessibilityLabel="Empty state with intents">
<s-grid gap="base" justifyItems="center" paddingBlock="large-400">
<s-box maxInlineSize="200px" maxBlockSize="200px">
<s-image
aspectRatio="1/0.5"
src="https://cdn.shopify.com/static/images/polaris/patterns/callout.png"
alt="Illustration showing product creation"
></s-image>
</s-box>
<s-grid justifyItems="center" maxInlineSize="450px" gap="base">
<s-stack alignItems="center">
<s-heading>No products yet</s-heading>
<s-paragraph>
Add products from your Shopify catalog to get started with quality auditing.
</s-paragraph>
</s-stack>
<s-button-group>
<s-button slot="secondary-actions">Browse products</s-button>
<s-button slot="primary-action">Create product</s-button>
</s-button-group>
</s-grid>
</s-grid>
</s-section>
<!--
JavaScript usage for the buttons:
// Browse products
shopify.intents.invoke('browse:shopify/Product');
// Create product
const activity = await shopify.intents.invoke('create:shopify/Product');
const response = await activity.complete;
if (response.code === 'ok') {
shopify.toast.show('Product created');
}
-->