When I first launched my Shopify store, I thought the hard part was getting the first sale. I was wrong. The real headache started the day after. My inbox was flooded with variations of the same question: "Where is my order?" I was struggling to keep up with support tickets while trying to manage inventory and marketing. I looked at existing tracking solutions, but many felt bloated, required deep theme modifications, or didn't quite match the post-purchase experience I wanted to create. So I decided to build something.
The Post-Purchase Problem
The "Where Is My Order" (WISMO) phenomenon is a universal pain point in e-commerce. When a customer completes a purchase, the transaction feels done to them. But for the merchant, the real work—and the real anxiety—has just begun.
Ignoring the post-purchase experience has tangible costs. Every WISMO ticket requires manual intervention. If you are paying for support staff or spending your own time answering emails, that is a direct hit to your margins. More importantly, a lack of visibility creates customer anxiety. If buyers have to dig through their emails to find a carrier tracking link, or if they are redirected to a generic, unbranded carrier page, the trust you built during checkout starts to erode.
To solve this, merchants generally look at two technical approaches for adding a tracking widget to their order status pages: modifying the theme files directly, or using Shopify's App Embed blocks. Both have distinct trade-offs.
Comparing the Technical Approaches
Approach 1: Direct Theme Modifications
The traditional way to add custom functionality to a Shopify store is to edit the Liquid templates directly—usually main-order.liquid or order-status.liquid.
The Pros: You have absolute control over the HTML structure and CSS. You can weave the tracking widget seamlessly into your existing theme grid without worrying about iframe restrictions or external script loading.
The Cons: This approach is incredibly fragile. The moment you update your theme, or switch to a new one, your custom tracking code is overwritten or lost. If you manage multiple stores, maintaining these custom Liquid edits across different themes becomes a logistical nightmare. It also tightly couples your tracking logic to your frontend theme, making it harder to iterate on the tracking experience independently.
Approach 2: Shopify App Embed Blocks (OS 2.0)
With the introduction of Online Store 2.0, Shopify introduced App Embed blocks. This allows apps to inject JavaScript and CSS into the storefront without requiring the merchant to edit theme code.
The Pros: Theme updates no longer break your tracking widget. Merchants can easily toggle the widget on or off from the theme editor. The logic is centralized in the app, meaning you can push updates to the tracking UI without requiring the merchant to touch a single line of code.
The Cons: You have to write highly defensive JavaScript. Because your widget is being injected into unknown theme environments, you must ensure your CSS doesn't leak and break the store's design, and that the theme's CSS doesn't break your widget. You also need to carefully manage asynchronous script loading to avoid blocking the main thread.
Building the App Embed Architecture
I chose the App Embed approach for its maintainability and resilience against theme updates. The core challenge was creating a lightweight, asynchronous widget that could read order data passed from Liquid and render a branded tracking experience.
Here is the foundational App Embed block I use to inject the tracking widget into the order status page:
{% comment %}
Renders shipment tracking widget on order status pages
{% endcomment %}
<div id="parcelglance-widget"
data-order-id="{{ order.id }}"
data-order-number="{{ order.name }}"
data-store-name="{{ shop.name }}"
style="min-height: 200px;">
</div>
<script>
(function() {
const script = document.createElement('script');
script.src = 'https://cdn.parcelglance.com/widget.js';
script.async = true;
script.onload = function() {
Parcelglance.init({
orderId: document.getElementById('parcelglance-widget').dataset.orderId,
orderNumber: document.getElementById('parcelglance-widget').dataset.orderNumber,
storeName: document.getElementById('parcelglance-widget').dataset.storeName,
container: 'parcelglance-widget',
});
};
document.head.appendChild(script);
})();
</script>
Breaking Down the Implementation
There are a few key decisions in this snippet worth highlighting:
-
Data Passing via Liquid: Instead of making an extra API call to fetch the order details on the frontend, we pass the
order.id,order.name, andshop.namedirectly intodata-attributes. This keeps the widget fast and reduces external network requests. -
Asynchronous Loading: The script is created dynamically and set to
async = true. This ensures that if the CDN is slow to respond, it doesn't block the rendering of the rest of the order status page. -
Container Fallback: The container
divhas amin-heightset. This prevents layout shift (CLS) when the script finally loads and renders the tracking UI, which is crucial for maintaining good Core Web Vitals scores.
On the backend, keeping this data in sync requires a reliable webhook handler. When Shopify fires an orders/fulfilled webhook, we verify the HMAC signature to ensure the request is legitimate, parse the payload, and upsert the tracking numbers and carrier details into our database. This ensures that when the frontend widget initializes, it has accurate, real-time status data to display.
Practical Results and Lessons Learned
Transitioning to an App Embed architecture completely changed how I handled store updates. When a merchant decided to switch from Dawn to a premium theme, the tracking widget simply kept working. The separation of concerns between the theme and the app logic paid off immensely.
What surprised me most was the psychological impact of a branded tracking page. Even though the underlying carrier data was the same, presenting it in a clean, familiar interface that matched the store's branding significantly reduced the feeling of uncertainty for the buyer.
If I were to change one thing about my initial build, it would be resisting the urge to over-engineer the dashboard. Early on, I tried to build a massive analytics suite for the tracking data. I quickly realized that merchants just wanted a reliable, zero-maintenance way to show customers where their package was. Stripping away the unnecessary complexity and focusing purely on a lightweight, fast-loading widget was the right call.
Wrapping Up
Choosing between theme edits and App Embeds ultimately comes down to how you value long-term maintainability. While direct theme edits offer immediate visual control, App Embeds provide a robust, update-proof foundation that scales much better as a store grows.
If you are dealing with post-purchase friction and want to see how an App Embed tracking widget can fit into your store without touching your theme code, I've packaged this approach into a free tool. You can check out the Shipment Tracking Widget at parcelglance.com.
Full disclosure: I'm the developer of Parcelglance. I built this because I needed it myself.
Top comments (0)