Files
antigravity-skills-reference/web-app/public/skills/shopify-development/references/extensions.md

10 KiB

Extensions Reference

Guide for building UI extensions and Shopify Functions.

Checkout UI Extensions

Customize checkout and thank-you pages with native-rendered components.

Extension Points

Block Targets (Merchant-Configurable):

  • purchase.checkout.block.render - Main checkout
  • purchase.thank-you.block.render - Thank you page

Static Targets (Fixed Position):

  • purchase.checkout.header.render-after
  • purchase.checkout.contact.render-before
  • purchase.checkout.shipping-option-list.render-after
  • purchase.checkout.payment-method-list.render-after
  • purchase.checkout.footer.render-before

Setup

shopify app generate extension --type checkout_ui_extension

Configuration (shopify.extension.toml):

api_version = "2026-01"
name = "gift-message"
type = "ui_extension"

[[extensions.targeting]]
target = "purchase.checkout.block.render"

[capabilities]
network_access = true
api_access = true

Basic Example

import {
  reactExtension,
  BlockStack,
  TextField,
  Checkbox,
  useApi,
} from "@shopify/ui-extensions-react/checkout";

export default reactExtension("purchase.checkout.block.render", () => (
  <Extension />
));

function Extension() {
  const [message, setMessage] = useState("");
  const [isGift, setIsGift] = useState(false);
  const { applyAttributeChange } = useApi();

  useEffect(() => {
    if (isGift) {
      applyAttributeChange({
        type: "updateAttribute",
        key: "gift_message",
        value: message,
      });
    }
  }, [message, isGift]);

  return (
    <BlockStack spacing="loose">
      <Checkbox checked={isGift} onChange={setIsGift}>
        This is a gift
      </Checkbox>
      {isGift && (
        <TextField
          label="Gift Message"
          value={message}
          onChange={setMessage}
          multiline={3}
        />
      )}
    </BlockStack>
  );
}

Common Hooks

useApi:

const { extensionPoint, shop, storefront, i18n, sessionToken } = useApi();

useCartLines:

const lines = useCartLines();
lines.forEach((line) => {
  console.log(line.merchandise.product.title, line.quantity);
});

useShippingAddress:

const address = useShippingAddress();
console.log(address.city, address.countryCode);

useApplyCartLinesChange:

const applyChange = useApplyCartLinesChange();

async function addItem() {
  await applyChange({
    type: "addCartLine",
    merchandiseId: "gid://shopify/ProductVariant/123",
    quantity: 1,
  });
}

Core Components

Layout:

  • BlockStack - Vertical stacking
  • InlineStack - Horizontal layout
  • Grid, GridItem - Grid layout
  • View - Container
  • Divider - Separator

Input:

  • TextField - Text input
  • Checkbox - Boolean
  • Select - Dropdown
  • DatePicker - Date selection
  • Form - Form wrapper

Display:

  • Text, Heading - Typography
  • Banner - Messages
  • Badge - Status
  • Image - Images
  • Link - Hyperlinks
  • List, ListItem - Lists

Interactive:

  • Button - Actions
  • Modal - Overlays
  • Pressable - Click areas

Admin UI Extensions

Extend Shopify admin interface.

Admin Action

Custom actions on resource pages.

shopify app generate extension --type admin_action
import {
  reactExtension,
  AdminAction,
  Button,
} from "@shopify/ui-extensions-react/admin";

export default reactExtension("admin.product-details.action.render", () => (
  <Extension />
));

function Extension() {
  const { data } = useData();

  async function handleExport() {
    const response = await fetch("/api/export", {
      method: "POST",
      body: JSON.stringify({ productId: data.product.id }),
    });
    console.log("Exported:", await response.json());
  }

  return (
    <AdminAction
      title="Export Product"
      primaryAction={<Button onPress={handleExport}>Export</Button>}
    />
  );
}

Targets:

  • admin.product-details.action.render
  • admin.order-details.action.render
  • admin.customer-details.action.render

Admin Block

Embedded content in admin pages.

import {
  reactExtension,
  BlockStack,
  Text,
  Badge,
} from "@shopify/ui-extensions-react/admin";

export default reactExtension("admin.product-details.block.render", () => (
  <Extension />
));

function Extension() {
  const { data } = useData();
  const [analytics, setAnalytics] = useState(null);

  useEffect(() => {
    fetchAnalytics(data.product.id).then(setAnalytics);
  }, []);

  return (
    <BlockStack>
      <Text variant="headingMd">Product Analytics</Text>
      <Text>Views: {analytics?.views || 0}</Text>
      <Text>Conversions: {analytics?.conversions || 0}</Text>
      <Badge tone={analytics?.trending ? "success" : "info"}>
        {analytics?.trending ? "Trending" : "Normal"}
      </Badge>
    </BlockStack>
  );
}

Targets:

  • admin.product-details.block.render
  • admin.order-details.block.render
  • admin.customer-details.block.render

POS UI Extensions

Customize Point of Sale experience.

Smart Grid Tile

Quick access action on POS home screen.

import {
  reactExtension,
  SmartGridTile,
} from "@shopify/ui-extensions-react/pos";

export default reactExtension("pos.home.tile.render", () => <Extension />);

function Extension() {
  function handlePress() {
    // Navigate to custom workflow
  }

  return (
    <SmartGridTile
      title="Gift Cards"
      subtitle="Manage gift cards"
      onPress={handlePress}
    />
  );
}

POS Modal

Full-screen workflow.

import {
  reactExtension,
  Screen,
  BlockStack,
  Button,
  TextField,
} from "@shopify/ui-extensions-react/pos";

export default reactExtension("pos.home.modal.render", () => <Extension />);

function Extension() {
  const { navigation } = useApi();
  const [amount, setAmount] = useState("");

  function handleIssue() {
    // Issue gift card
    navigation.pop();
  }

  return (
    <Screen name="Gift Card" title="Issue Gift Card">
      <BlockStack>
        <TextField label="Amount" value={amount} onChange={setAmount} />
        <TextField label="Recipient Email" />
        <Button onPress={handleIssue}>Issue</Button>
      </BlockStack>
    </Screen>
  );
}

Customer Account Extensions

Customize customer account pages.

Order Status Extension

import {
  reactExtension,
  BlockStack,
  Text,
  Button,
} from "@shopify/ui-extensions-react/customer-account";

export default reactExtension(
  "customer-account.order-status.block.render",
  () => <Extension />,
);

function Extension() {
  const { order } = useApi();

  function handleReturn() {
    // Initiate return
  }

  return (
    <BlockStack>
      <Text variant="headingMd">Need to return?</Text>
      <Text>Start return for order {order.name}</Text>
      <Button onPress={handleReturn}>Start Return</Button>
    </BlockStack>
  );
}

Targets:

  • customer-account.order-status.block.render
  • customer-account.order-index.block.render
  • customer-account.profile.block.render

Shopify Functions

Serverless backend customization.

Function Types

Discounts:

  • order_discount - Order-level discounts
  • product_discount - Product-specific discounts
  • shipping_discount - Shipping discounts

Payment Customization:

  • Hide/rename/reorder payment methods

Delivery Customization:

  • Custom shipping options
  • Delivery rules

Validation:

  • Cart validation rules
  • Checkout validation

Create Function

shopify app generate extension --type function

Order Discount Function

// input.graphql
query Input {
  cart {
    lines {
      quantity
      merchandise {
        ... on ProductVariant {
          product {
            hasTag(tag: "bulk-discount")
          }
        }
      }
    }
  }
}

// function.js
export default function orderDiscount(input) {
  const targets = input.cart.lines
    .filter(line => line.merchandise.product.hasTag)
    .map(line => ({
      productVariant: { id: line.merchandise.id }
    }));

  if (targets.length === 0) {
    return { discounts: [] };
  }

  return {
    discounts: [{
      targets,
      value: {
        percentage: {
          value: 10  // 10% discount
        }
      }
    }]
  };
}

Payment Customization Function

export default function paymentCustomization(input) {
  const hidePaymentMethods = input.cart.lines.some(
    (line) => line.merchandise.product.hasTag,
  );

  if (!hidePaymentMethods) {
    return { operations: [] };
  }

  return {
    operations: [
      {
        hide: {
          paymentMethodId: "gid://shopify/PaymentMethod/123",
        },
      },
    ],
  };
}

Validation Function

export default function cartValidation(input) {
  const errors = [];

  // Max 5 items per cart
  if (input.cart.lines.length > 5) {
    errors.push({
      localizedMessage: "Maximum 5 items allowed per order",
      target: "cart",
    });
  }

  // Min $50 for wholesale
  const isWholesale = input.cart.lines.some(
    (line) => line.merchandise.product.hasTag,
  );

  if (isWholesale && input.cart.cost.totalAmount.amount < 50) {
    errors.push({
      localizedMessage: "Wholesale orders require $50 minimum",
      target: "cart",
    });
  }

  return { errors };
}

Network Requests

Extensions can call external APIs.

import { useApi } from "@shopify/ui-extensions-react/checkout";

function Extension() {
  const { sessionToken } = useApi();

  async function fetchData() {
    const token = await sessionToken.get();

    const response = await fetch("https://your-app.com/api/data", {
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
      },
    });

    return await response.json();
  }
}

Best Practices

Performance:

  • Lazy load data
  • Memoize expensive computations
  • Use loading states
  • Minimize re-renders

UX:

  • Provide clear error messages
  • Show loading indicators
  • Validate inputs
  • Support keyboard navigation

Security:

  • Verify session tokens on backend
  • Sanitize user input
  • Use HTTPS for all requests
  • Don't expose sensitive data

Testing:

  • Test on development stores
  • Verify mobile/desktop
  • Check accessibility
  • Test edge cases

Resources