Shopify Customer Account Extensions: Unifying Legacy & In-Store Orders

Overview

A leading Spanish footwear retailer with both online and physical store channels needed their Shopify Plus customer account pages to display order history from three separate systems: native Shopify orders, legacy e-commerce platform orders (pre-migration), and physical store POS transactions. The solution: two custom Shopify Customer Account UI Extensions that fetch, transform, and render external order data directly inside the Shopify customer portal — seamlessly alongside native orders.

The Challenge

When retailers migrate to Shopify Plus, they gain a modern storefront — but they lose visibility into historical orders placed on the previous platform. Customers log in and see an empty order history, even though they may have years of purchases. The same gap exists for in-store purchases: a customer who buys in a physical location sees nothing in their online account.

This retailer operates multiple physical stores across Spain alongside their Shopify Plus storefront. Their legacy platform still held years of order data, and their retail POS system tracked in-store transactions independently. The brief was clear: give customers a single, unified order history inside their Shopify account — without migrating order data into Shopify itself.

The technical constraints made this interesting. Shopify's Customer Account UI Extensions run in a sandboxed environment with no access to the DOM, no arbitrary HTTP libraries, and a limited component set. Authentication context is provided through Shopify hooks, but customer email isn't always immediately available. And the external APIs serving legacy and store data were PHP-based REST endpoints with their own response format — not Shopify's GraphQL.

Technical Architecture

The solution is built as a Shopify app with two independent Customer Account UI Extensions, both targeting the customer-account.order-index.block.render extension point. This injects custom order sections directly into the customer's order list view.

Stack: React 18, Shopify UI Extensions SDK (v2025.7), Customer Account API (v2025-04), external REST API integration.

Each extension follows the same architecture:

  1. Customer identification — A 3-tier fallback chain resolves the logged-in customer's email
  2. External data fetch — REST calls to the legacy/POS system with the customer's email and ID
  3. Data transformation — Response parsing, filtering, status translation (Spanish to English), date and currency formatting
  4. Rendering — Dual-view UI (list or gallery) using Shopify's native UI Extension components

Key Technical Decisions

Three-Tier Customer Authentication

Customer email isn't always available through a single hook. The extension implements a cascading resolution strategy:

  1. Try useAuthenticatedAccountCustomer() — the primary Shopify hook
  2. Fall back to useApi() — an alternative access path
  3. Query the Customer Account GraphQL API directly — shopify://customer-account/api/2025-04/graphql.json

This triple fallback ensures the extension works reliably across different authentication states and session configurations. Without it, a percentage of customers would see empty sections.

Two Extensions, Not One

Rather than building a single configurable extension that handles both legacy and store orders, the project uses two independent extensions with near-identical code. This was deliberate: each extension can be enabled, disabled, positioned, and configured independently through the Shopify theme editor. A merchant can show legacy orders without store orders, or vice versa, without touching code.

Sandboxed External API Calls

Shopify's extension sandbox doesn't allow arbitrary HTTP libraries. All external API calls use the native fetch available in the extension runtime. The response handling is defensive — the legacy APIs return JSON wrapped in text responses, requiring careful parsing:

const data = JSON.parse(responseText);
let orderList = [];
if (data.success && data.data && typeof data.data === 'object') {
  orderList = Object.values(data.data);
}

Legacy orders are filtered where b_isShopify === 0 to exclude any that already exist as native Shopify orders — preventing duplicates in the customer's view.

Status Translation Layer

The external systems return order statuses in Spanish. Rather than requiring backend changes, the extension handles localisation at the UI layer with a clean mapping:

'Enviado' → 'On its way'
'Entregado' → 'Delivered'
'Pendiente' → 'Confirmed'
'Cancelado' → 'Cancelled'

This keeps the extension self-contained and avoids coupling to the legacy system's internals.

UI Implementation

Both extensions support two view modes — list and gallery — toggleable by the customer with custom icon buttons.

List view uses Shopify's ResourceItem component with InlineLayout for multi-column rows: a 64px product thumbnail, order details (number, status, date), and the order total. This gives native Shopify hover and interaction states for free.

Gallery view renders a 2-column Grid with larger thumbnails and centred text — better for visually browsing past purchases, especially for a footwear retailer where product images matter.

Smart date formatting shows "Dec 1" for current-year orders and "Nov 26, 2024" for older ones. Currency renders in Euro format. All interactive elements carry accessibilityLabel and accessibilityDescription attributes.

The entire section hides itself gracefully if no orders are found — no empty states, no confusion.

Configuration

Each extension exposes four settings configurable through the Shopify theme editor:

  • Section title — Customisable heading text
  • Default view — List or gallery on first load
  • Placeholder image — Fallback thumbnail URL (120x120px recommended)
  • Hint text — Explanatory copy displayed below the order list

This means the merchant's team can adjust copy and behaviour without developer involvement.

Outcome

The project delivered two production-ready Shopify Customer Account UI Extensions — approximately 680 lines of React across two modules. Customers now see their complete purchase history in one place: native Shopify orders, legacy platform orders, and physical store transactions. No order data migration was required. The extensions deploy independently via Shopify CLI and are configurable through the standard theme editor.

Skills Demonstrated

  • Shopify Platform: Customer Account UI Extensions (v2025-04), Extension Points, useAuthenticatedAccountCustomer hook, Customer Account GraphQL API, Shopify CLI deployment, TOML-based app configuration
  • Frontend: React 18, Shopify UI Extensions component library (ResourceItem, Grid, InlineLayout, BlockStack), controlled state management with hooks, dual-view rendering
  • Integration: Cross-system REST API integration, defensive JSON parsing, data filtering and deduplication, multi-tier authentication fallback
  • UX: Accessible UI (ARIA labels), smart date/currency formatting, graceful empty states, configurable merchant settings, Spanish-English status localisation