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.
Uptick ships as a standalone repo that you pull into your Shopify CLI project.
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.
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 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");
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 });