Shopify UI Extensions Integration Guide

Uptick Shopify Extensibility plugs straight into any Shopify UI Extension and, with just a few lines of code, lets you display dynamic, API-driven offers on both the Thank You and Order Status pages. Add the repo as a submodule, register two render targets, and you’ll have personalized post-purchase upsells live in minutes.


Features

  • Dynamic Offer Rendering: Display offers using minimal coding on Shopify UI Checkout extensions.
  • Error Handling and Callbacks: Includes hooks for handling errors and warning events.
  • Dynamic Content Support: Renders a variety of offer types to maximize conversions.
  • Api Flow Management: Fetches responses and returns data to dynamically render offers.

Installation

Uptick ships as a standalone repo that you pull into your Shopify CLI project.

Git Submodule instructions:

To add the submodule directly to your Shopify CLI Extension project. Replace {extension-name} in the path below with your actual extension name. Then, run the following commands from your projects root directory. This will import the repository as uptick-extension into your Shopify CLI Extension project.

git submodule add https://github.com/uptick-ads/shopify-extensibility.git extensions/{extension-name}/src/uptick-extension
git submodule update --init --recursive

This clones the Uptick code into extensions/<extension-name>/src/uptick-extension and pins it to the current commit. Whenever you want the latest fixes, just run git submodule update --remote and commit the version bump.


Extension Setup

Now that the submodule exists on your Shopify CLI Extension project, the extension configuration needs to be updated with the offer targeting locations. Uptick recommends the following extension settings to maximize conversions and ensure proper setup. In your shopify.extensions.toml ensure you have the following settings.

# Use this api version or later
api_version = "2025-04"

# Capabilities
[extensions.capabilities]
api_access = true
network_access = true
block_progress = false

# Targets
# Thank you page
[[extensions.targeting]]
target = "purchase.thank-you.block.render"
module = "./src/ThankYou.jsx" # Can be customized to your project file name
export = "uptickThankYouBlockRender"
default_placement = "ORDER_STATUS1"

# Order status page
[[extensions.targeting]]
target = "customer-account.order-status.block.render"
module = "./src/OrderStatus.jsx" # Can be customized to your project file name
export = "uptickOrderStatusBlockRender"
default_placement = "ORDER_SUMMARY1"

Extension Targets (Thank You and Order Status)

Extension Targets are the pages you want to execute code on. For Uptick, we have two targets you need to generate files for. If you already have files for the Thank You and Order Status page, you can use those files, just update the module reference in shopify.extensions.toml for each of the targets in the previous section. If you don’t have any files created, we recommend ThankYou.jsx and OrderStatus.jsx to align with the module configuration above.

Below is a full code sample you can use to execute Uptick offers. Comments are inline to add additional context.

File: {root}/extensions/{extension-name}/src/ThankYou.jsx

import {
  useState,
  useEffect
} from "react";

import {
  useApi,
  reactExtension
  // On the Order Status Page the import would be: `from "@shopify/ui-extensions-react/customer-account";`
} from "@shopify/ui-extensions-react/checkout";

// Uptick Api Class
import UptickApi from "./uptick-extension/services/api.js";

// Uptick Flow Component
import UptickFlow from "./extraction/components/UptickFlow.jsx";

// Has to be initialized outside of the component to maintain integrationId
const uptickApi = new UptickApi({ integrationId: "{Your Integration Id}" });

// This is specifically for the Thank You Page
const uptickThankYouBlockRender = reactExtension("purchase.thank-you.block.render", () => <Extension />);
export { uptickThankYouBlockRender };

// For Order Status page
// const uptickOrderStatusBlockRender = reactExtension("customer-account.order-status.block.render", () => <Extension />);
// export { uptickOrderStatusBlockRender };

function Extension() {
  const shopApi = useApi(); // Initial Shopify's api
  const [loading, setLoading] = useState(true); // Create a loading state
  const [offer, setOffer] = useState(null); // Create an offer state
  uptickApi.setup({ shopApi, setLoading }); // Populate Uptick Api with session information

  // Get the first offer
  useEffect(() => {
    (async () => {
      // For Thank You page, the value here is: order_confirmation
      // For Order Status page, the value here is: order_status
      let offer = await uptickApi.getInitialOffer("order_confirmation");
      setOffer(offer);
    })();
  }, []);

  // Used to get the second offer
  // Called from UptickFlow
  function rejectOffer(rejectURL) {
    (async () => {
      let offer = await uptickApi.getNextOffer(rejectURL);
      setOffer(offer);
    })();
  }

  // Render an Uptick offer
  return (
    <UptickFlow
      offer={offer}
      rejectOffer={rejectOffer}
      loading={loading} />
  );
}

Please note, there are only three changes between the Thank You page and Order Status page. They are commented above, but for clarity, they specific changes are listed here also.

Thank You page (ThankYou.jsx)

// import from the checkout path
import {
  useApi,
  reactExtension
} from "@shopify/ui-extensions-react/checkout";

// The extension path that's imported
const uptickThankYouBlockRender = reactExtension("purchase.thank-you.block.render", () => <Extension />);
export { uptickThankYouBlockRender };

// When calling the initial offer, pass in order_confirmation
let offer = await uptickApi.getInitialOffer("order_confirmation");

Order Status page (OrderStatus.jsx)

// import from the customer-account path
import {
  useApi,
  reactExtension
} from "@shopify/ui-extensions-react/customer-account";

// The extension path that's imported
const uptickOrderStatusBlockRender = reactExtension("customer-account.order-status.block.render", () => <Extension />);
export { uptickOrderStatusBlockRender };

// When calling the initial offer, pass in order_status
let offer = await uptickApi.getInitialOffer("order_status");

Api Logging

To capture errors to a service like Sentry or Bugsnag, the Api can be initialized with two extra methods. If not explicitly initialized, these will default logging to the console. To initialize the Api with captureWarning and captureException callbacks use the following method signatures:

  // Method signatures
  let captureWarning = (warningMessage, context) => {...};
  let captureException = (error, context) => {...};

  // Example using Sentry
  // Sentry initialization here ...
  // Import exception method
  import { captureException, captureMessage } from "@sentry/browser";
  const uptickApi = new UptickApi({ integrationId: "{Your Integration Id}", captureError: captureException, captureWarning: captureMessage });

Troubleshooting Tips

  • Support: If issues persist, or you need additional help, please reach out to our technical support team for further assistance.