Back to blog
shopify checkout-extensibility shopify-development checkout-ui-extensions shopify-plus javascript api

Shopify Checkout Extensibility: Complete Guide to Custom Checkout in 2025

Learn how to customize Shopify checkout with Checkout Extensibility. Build checkout UI extensions, add custom fields, and create unique checkout experiences without checkout.liquid.

2 min read

Shopify deprecated checkout.liquid in August 2024. If you’re still using it, you’re on borrowed time. Checkout Extensibility is the new way to customize Shopify checkout—and honestly, it’s better. Here’s everything you need to know to migrate or build new checkout customizations.

What Changed (And Why It Matters)

Before Checkout Extensibility, customizing checkout required:

  • Shopify Plus for checkout.liquid access
  • Direct Liquid/CSS/JS edits to the checkout template
  • Careful testing (breaking checkout = losing sales)
  • Manual maintenance with every Shopify update

The problems with checkout.liquid:

  1. Security risks (direct code injection)
  2. Performance issues (unoptimized custom code)
  3. Upgrade friction (custom code broke with updates)
  4. Limited functionality (no backend logic)

Checkout Extensibility solves all of these:

  • Sandboxed extensions (can’t break checkout)
  • Optimized runtime (Shopify controls performance)
  • Upgrade-safe (extensions adapt to checkout changes)
  • Full functionality (UI extensions + backend logic)

What You Can Build

Checkout Extensibility supports three types of customizations:

1. Checkout UI Extensions

Add custom UI elements to checkout:

  • Custom input fields (gift messages, delivery instructions)
  • Upsells and cross-sells
  • Trust badges and guarantees
  • Loyalty point displays
  • Custom shipping options
  • Order notes and preferences

2. Shopify Functions

Backend logic that runs during checkout:

  • Custom discounts (complex rules, tiered pricing)
  • Shipping rate customization (rules-based shipping)
  • Payment method customization (show/hide based on conditions)
  • Cart validation (quantity limits, product restrictions)

3. Web Pixels

Tracking and analytics:

  • Custom analytics events
  • Conversion tracking
  • Marketing pixels
  • Customer behavior tracking

Getting Started

Prerequisites

You need:

  • Shopify CLI installed (npm install -g @shopify/cli)
  • Node.js 18+
  • A Shopify Partner account
  • A development store (or Plus store for production)

Note: Checkout UI Extensions are available on ALL Shopify plans (not just Plus). This is a huge change—you no longer need Plus to customize checkout appearance.

Create Your First Extension

# Create a new app
shopify app init

# Navigate to your app
cd your-app-name

# Generate a checkout UI extension
shopify app generate extension --type checkout_ui_extension

This creates a scaffold with:

extensions/
└── checkout-ui/
    ├── src/
    │   └── Checkout.jsx
    ├── locales/
    │   └── en.default.json
    └── shopify.extension.toml

Basic Extension Example

Here’s a simple “Gift Message” field extension:

// extensions/checkout-ui/src/Checkout.jsx
import {
  extension,
  BlockStack,
  TextField,
  useMetafield,
  useApplyMetafieldsChange,
} from '@shopify/ui-extensions-react/checkout';

export default extension('purchase.checkout.block.render', (root, api) => {
  return <GiftMessage />;
});

function GiftMessage() {
  const giftMessage = useMetafield({
    namespace: 'custom',
    key: 'gift_message',
  });
  
  const applyMetafieldsChange = useApplyMetafieldsChange();
  
  const handleChange = (value) => {
    applyMetafieldsChange({
      type: 'updateMetafield',
      namespace: 'custom',
      key: 'gift_message',
      valueType: 'string',
      value,
    });
  };
  
  return (
    <BlockStack>
      <TextField
        label="Gift message (optional)"
        value={giftMessage?.value || ''}
        onChange={handleChange}
        maxLength={200}
      />
    </BlockStack>
  );
}

Deploy and Test

# Start development server
shopify app dev

# Deploy to production
shopify app deploy

Real-World Use Cases

Use Case 1: Checkout Upsells

Add post-add-to-cart upsells in checkout:

import {
  extension,
  BlockStack,
  InlineStack,
  Image,
  Text,
  Button,
  useCartLines,
  useApplyCartLinesChange,
} from '@shopify/ui-extensions-react/checkout';

function CheckoutUpsell() {
  const cartLines = useCartLines();
  const applyCartLinesChange = useApplyCartLinesChange();
  
  // Show upsell if cart value is under $100
  const cartTotal = cartLines.reduce((sum, line) => 
    sum + (line.cost.totalAmount.amount * 1), 0
  );
  
  if (cartTotal >= 100) return null;
  
  const handleAddUpsell = async () => {
    await applyCartLinesChange({
      type: 'addCartLine',
      merchandiseId: 'gid://shopify/ProductVariant/12345',
      quantity: 1,
    });
  };
  
  return (
    <BlockStack border="base" padding="base" borderRadius="base">
      <Text emphasis="bold">Add $20 more for free shipping!</Text>
      <InlineStack>
        <Image source="product-image.jpg" />
        <BlockStack>
          <Text>Recommended Product</Text>
          <Text appearance="subdued">$25.00</Text>
          <Button onPress={handleAddUpsell}>Add to Order</Button>
        </BlockStack>
      </InlineStack>
    </BlockStack>
  );
}

Use Case 2: Delivery Date Picker

Let customers choose delivery date:

import {
  extension,
  DatePicker,
  BlockStack,
  Text,
  useMetafield,
  useApplyMetafieldsChange,
} from '@shopify/ui-extensions-react/checkout';

function DeliveryDatePicker() {
  const deliveryDate = useMetafield({
    namespace: 'custom',
    key: 'delivery_date',
  });
  
  const applyChange = useApplyMetafieldsChange();
  
  // Calculate min date (3 days from now)
  const minDate = new Date();
  minDate.setDate(minDate.getDate() + 3);
  
  // Max date (30 days from now)
  const maxDate = new Date();
  maxDate.setDate(maxDate.getDate() + 30);
  
  return (
    <BlockStack>
      <Text emphasis="bold">Choose Delivery Date</Text>
      <DatePicker
        selected={deliveryDate?.value}
        onChange={(value) => {
          applyChange({
            type: 'updateMetafield',
            namespace: 'custom',
            key: 'delivery_date',
            valueType: 'string',
            value,
          });
        }}
        disabled={[
          { start: '2000-01-01', end: minDate.toISOString().split('T')[0] },
        ]}
      />
    </BlockStack>
  );
}

Use Case 3: Custom Discount with Shopify Functions

Create a “Buy 2, Get 1 Free” discount:

// extensions/product-discount/src/run.js
export function run(input) {
  const discounts = [];
  
  // Group line items by product
  const productGroups = {};
  
  for (const line of input.cart.lines) {
    const productId = line.merchandise.product.id;
    if (!productGroups[productId]) {
      productGroups[productId] = [];
    }
    productGroups[productId].push(line);
  }
  
  // Apply "Buy 2 Get 1 Free" logic
  for (const [productId, lines] of Object.entries(productGroups)) {
    const totalQuantity = lines.reduce((sum, line) => sum + line.quantity, 0);
    const freeItems = Math.floor(totalQuantity / 3);
    
    if (freeItems > 0) {
      // Find the lowest priced variant to discount
      const sortedLines = [...lines].sort(
        (a, b) => a.cost.amountPerQuantity.amount - b.cost.amountPerQuantity.amount
      );
      
      let remainingFree = freeItems;
      for (const line of sortedLines) {
        if (remainingFree <= 0) break;
        
        const discountQuantity = Math.min(remainingFree, line.quantity);
        remainingFree -= discountQuantity;
        
        discounts.push({
          targets: [{ cartLine: { id: line.id } }],
          value: {
            percentage: { value: 100 },
          },
          message: 'Buy 2, Get 1 Free!',
        });
      }
    }
  }
  
  return { discounts };
}

Extension Placement Targets

Checkout Extensibility defines specific locations where you can add UI:

TargetLocationUse Case
purchase.checkout.header.render-afterAfter headerAnnouncements, promos
purchase.checkout.block.renderMain contentCustom fields, upsells
purchase.checkout.contact.render-afterAfter contact infoAdditional contact fields
purchase.checkout.shipping-address.render-afterAfter shippingDelivery preferences
purchase.checkout.payment-method.render-afterAfter paymentPayment-related info
purchase.checkout.footer.render-beforeBefore footerTrust badges, guarantees

Migration from checkout.liquid

If you’re currently using checkout.liquid, here’s the migration path:

Step 1: Audit Current Customizations

List everything you’ve customized:

  • Custom CSS styling
  • Additional form fields
  • Custom JavaScript
  • Third-party scripts
  • Analytics tracking

Step 2: Map to Extensibility Features

checkout.liquid FeatureExtensibility Equivalent
Custom CSSUI Extension components (styled)
Custom form fieldsUI Extension with TextField/Select
Custom JS logicShopify Functions
Analytics scriptsWeb Pixels
Upsell widgetsUI Extension with cart modification

Step 3: Build and Test

  1. Create extensions for each customization
  2. Test thoroughly in development store
  3. Deploy to staging/preview
  4. Gradual rollout to production

Timeline

Shopify’s deadline for checkout.liquid deprecation was August 2024. If you haven’t migrated:

  • Contact Shopify support about your migration timeline
  • Prioritize critical customizations first
  • Consider hiring a Shopify expert if needed

Performance Considerations

Checkout Extensibility is designed for performance, but you can still impact it:

Do’s

  • Keep extensions lightweight
  • Use Shopify’s UI components (optimized)
  • Lazy load heavy content
  • Minimize API calls

Don’ts

  • Don’t fetch large datasets in checkout
  • Don’t add multiple similar extensions
  • Don’t use custom fonts (use system fonts)
  • Don’t block checkout completion

Monitoring

Use Shopify’s extension analytics to monitor:

  • Extension load time
  • Error rates
  • User interactions
  • Conversion impact

Common Gotchas

1. Metafield Namespaces

Custom metafields need the custom namespace for checkout access:

{
  namespace: 'custom',  // Must be 'custom'
  key: 'your_field',
  valueType: 'string',
}

2. Cart Line Modification

You can add/remove cart lines, but some restrictions apply:

  • Can’t modify prices directly (use Shopify Functions)
  • Can’t remove the last item
  • Quantity limits still apply

3. Testing Limitations

Development mode has limitations:

  • Some features only work in deployed extensions
  • Payment testing requires real payment methods
  • Shipping rates need real carrier accounts

Tools and Resources

Official Documentation

Development Tools

  • Shopify CLI: Essential for extension development
  • Polypane: Test checkout across viewports
  • Shopify GraphiQL App: Test Admin API queries

Learning Resources

What to Watch For

Future Changes

Shopify continues to evolve Checkout Extensibility:

  • New extension targets being added regularly
  • More Shopify Functions types coming
  • Improved performance and capabilities

Stay updated via:

  • Shopify Dev newsletter
  • Changelog RSS feed
  • Partner dashboard announcements

Plus vs Non-Plus

Most features are available on all plans now, but some remain Plus-only:

  • Some advanced Shopify Functions
  • B2B checkout customizations
  • Script Editor (legacy)

Check Shopify’s plan comparison for current feature availability.

Bottom Line

Checkout Extensibility is the future of Shopify checkout customization. It’s more powerful, more secure, and more maintainable than checkout.liquid ever was.

My recommendation:

  • If you’re on checkout.liquid: Migrate now. The deadline has passed, and you’re accumulating technical debt.
  • If you’re building new: Start with Extensibility. Don’t learn the old way.
  • If you need complex customizations: Consider hiring a specialist. Checkout is too important to get wrong.

The learning curve is steeper than Liquid, but the results are worth it. You get upgradable, performant, secure checkout customizations that won’t break with every Shopify update.


New to Shopify development? Try Shopify with a 14-day free trial and start with Shopify’s Partner program for free development stores and learning resources.

Share this article