Resource details layout

Lets merchants create, view, and edit resource objects.

Discuss on GitHub

How it helps merchants

Product details page
  1. The page header provides easy access to actions and navigation. It spans the full width of the page to show merchants that these actions represent the page as a whole.
  2. The main content is split in two columns, primary content to the left and secondary content to the right. The primary content occupies two thirds of the page to give more space to what’s most important most of the time.
  3. Content is placed in cards, and similar content is grouped in the same card. This helps merchant find and focus on specific subtasks.

Use when merchants need to:

View and edit resource objects
This pattern is typically paired with the resource index layout pattern. Together they create one of the Shopify admin’s strongest patterns. Merchants start learning how to use it when they create their first product, and then continue to use it for other essential resource objects such as orders and customers. Use it when merchants need to manage any individual resource object, including niched ones such as discounts, shipping labels, and newsletters.
Create resource objects
Using the resource detail layout when merchants create new resources teaches them both what a resource page looks like and how edit one later.

Using this pattern

This pattern uses the Card, BlockStack, InlineGrid and Page components.

// This example is for guidance purposes. Copying it will come with caveats.
function ResourceDetailsLayout() {
  const SkeletonLabel = (props) => {
    return (
      <Box
        background="bg-fill-tertiary"
        minHeight="1rem"
        maxWidth="5rem"
        borderRadius="base"
        {...props}
      />
    );
  };
  return (
    <Page
      backAction={{ content: "Products", url: "/products" }}
      title="Product"
      secondaryActions={[
        {
          content: "Duplicate",
          icon: DuplicateIcon,
          accessibilityLabel: "Secondary action label",
          onAction: () => alert("Duplicate action"),
        },
        {
          content: "Archive",
          icon: ArchiveIcon,
          accessibilityLabel: "Secondary action label",
          onAction: () => alert("Archive action"),
        },
        {
          content: "Delete",
          icon: DeleteIcon,
          destructive: true,
          accessibilityLabel: "Secondary action label",
          onAction: () => alert("Delete action"),
        },
      ]}
      pagination={{
        hasPrevious: true,
        hasNext: true,
      }}
    >
      <InlineGrid columns={{ xs: 1, md: "2fr 1fr" }} gap="400">
        <BlockStack gap="400">
          <Card roundedAbove="sm">
            <BlockStack gap="400">
              <SkeletonLabel />
              <Box border="divider" borderRadius="base" minHeight="2rem" />
              <SkeletonLabel maxWidth="8rem" />
              <Box border="divider" borderRadius="base" minHeight="20rem" />
            </BlockStack>
          </Card>
          <Card roundedAbove="sm">
            <BlockStack gap="400">
              <SkeletonDisplayText size="small" />
              <InlineGrid columns={{ xs: 1, md: 2 }}>
                <Box border="divider" borderRadius="base" minHeight="10rem" />
                <Box border="divider" borderRadius="base" minHeight="10rem" />
              </InlineGrid>
            </BlockStack>
          </Card>
        </BlockStack>
        <BlockStack gap={{ xs: "400", md: "200" }}>
          <Card roundedAbove="sm">
            <BlockStack gap="400">
              <SkeletonDisplayText size="small" />
              <Box border="divider" borderRadius="base" minHeight="2rem" />
              <Box>
                <Bleed marginInline={{ xs: 400, sm: 500 }}>
                  <Divider />
                </Bleed>
              </Box>
              <SkeletonLabel />
              <Divider />
              <SkeletonBodyText />
            </BlockStack>
          </Card>
          <Card roundedAbove="sm">
            <BlockStack gap="400">
              <SkeletonLabel />
              <Box border="divider" borderRadius="base" minHeight="2rem" />
              <SkeletonLabel maxWidth="4rem" />
              <Box border="divider" borderRadius="base" minHeight="2rem" />
              <SkeletonLabel />
              <SkeletonBodyText />
            </BlockStack>
          </Card>
        </BlockStack>
      </InlineGrid>
    </Page>
  )
}

Useful to know

  • Always use the default width. Full width tends to waste space and make the page harder to parse.

    Details page with margins on either side of the main content
  • Group similar content in the same card.

    Diagram showing multiple cards compared to a single card that groups the same content
  • Put information that defines the resource object in the primary column.

    Product detail example
  • Put supporting information such as status, metadata, and summaries in the secondary column.

    Product details page with the secondary column outlined
  • Arrange content in order of importance.

    Product details page with “Very important section” card placed above “Somewhat important section” card
  • Place unique page actions at the top of the actions list and typical object actions at the bottom.

    Popover with unique page actions placed at the top, and typical object actions placed at the bottom
  • The Resource index layout pattern is a complement to the resource detail layout pattern.
  • Learn about the meaning of “resources” on the Resource list component page
  • Learn more about Layout in the app design guidelines.

    On this page