Building a Branded Tracking Page for Your Store
Every time a customer clicks a carrier’s tracking link, they leave your site. That’s a missed opportunity for engagement, branding, and reducing support load. A branded tracking page keeps customers on your domain and provides a better experience. Let’s build one.
Why Build a Branded Tracking Page?
The Problem with Carrier Tracking Pages
When you send customers to the carrier’s tracking page:
- They leave your brand experience
- They see the carrier’s ads, not your promotions
- They can’t easily contact your support team
- If tracking looks confusing, they blame you — not the carrier
The Benefits of a Branded Page
| Benefit | Impact |
|---|---|
| Keep customers on your site | +15% repeat page views |
| Reduce support tickets | -40% tracking inquiries |
| Upsell opportunities | Show related products |
| Brand consistency | Professional experience |
| Data collection | Track customer engagement |
Architecture
Customer clicks tracking link
↓
Your branded tracking page
↓
JavaScript fetches tracking data from your API
↓
Your API calls WhereParcel → Returns standardized data
↓
Render tracking timeline + brand elements
Step 1: Backend API Endpoint
Create an API endpoint that proxies WhereParcel requests:
// routes/tracking.js
const express = require('express');
const router = express.Router();
router.get('/api/track/:trackingNumber', async (req, res) => {
const { trackingNumber } = req.params;
try {
// Look up order by tracking number
const order = await db.orders.findByTracking(trackingNumber);
// Fetch tracking data from WhereParcel
const tracking = await fetch('https://api.whereparcel.com/v2/track', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.WHEREPARCEL_API_KEY}:${process.env.WHEREPARCEL_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
trackingItems: [{ carrier: order?.carrier || 'auto', trackingNumber }],
}),
}).then(r => r.json());
res.json({
order: order ? {
id: order.id,
items: order.items.map(i => ({
name: i.name,
image: i.imageUrl,
quantity: i.quantity,
})),
} : null,
tracking: tracking.data,
});
} catch (error) {
res.status(500).json({ error: 'Unable to fetch tracking data' });
}
});
module.exports = router;
Step 2: Tracking Page HTML
Create the tracking page structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Track Your Order | YourStore</title>
<style>
/* See Step 4 for full styles */
</style>
</head>
<body>
<header class="tracking-header">
<a href="/" class="logo">
<img src="/logo.svg" alt="YourStore" />
</a>
<a href="/support" class="support-link">Need help?</a>
</header>
<main class="tracking-container">
<!-- Search form (if no tracking number in URL) -->
<div id="search-section" class="search-section">
<h1>Track Your Order</h1>
<form id="tracking-form">
<input
type="text"
id="tracking-input"
placeholder="Enter your tracking number"
required
/>
<button type="submit">Track</button>
</form>
</div>
<!-- Results -->
<div id="results-section" class="results-section" style="display:none">
<div id="status-banner" class="status-banner"></div>
<div id="order-items" class="order-items"></div>
<div id="tracking-timeline" class="tracking-timeline"></div>
</div>
<!-- Loading state -->
<div id="loading" class="loading" style="display:none">
<div class="spinner"></div>
<p>Fetching tracking information...</p>
</div>
</main>
<footer class="tracking-footer">
<p>© 2026 YourStore. All rights reserved.</p>
</footer>
<script src="/tracking.js"></script>
</body>
</html>
Step 3: Frontend JavaScript
Fetch and render tracking data:
// public/tracking.js
const STATUS_CONFIG = {
pending: { label: 'Pending', icon: 'clock', color: '#6b7280' },
info_received: { label: 'Info Received', icon: 'file', color: '#3b82f6' },
picked_up: { label: 'Picked Up', icon: 'package', color: '#3b82f6' },
in_transit: { label: 'In Transit', icon: 'truck', color: '#f59e0b' },
out_for_delivery: { label: 'Out for Delivery', icon: 'map-pin', color: '#8b5cf6' },
delivered: { label: 'Delivered', icon: 'check-circle', color: '#10b981' },
exception: { label: 'Exception', icon: 'alert', color: '#ef4444' },
};
// Check URL for tracking number
const urlParams = new URLSearchParams(window.location.search);
const trackingNumber = urlParams.get('tn');
if (trackingNumber) {
document.getElementById('tracking-input').value = trackingNumber;
fetchTracking(trackingNumber);
}
// Form submission
document.getElementById('tracking-form').addEventListener('submit', (e) => {
e.preventDefault();
const tn = document.getElementById('tracking-input').value.trim();
if (tn) {
// Update URL without reload
window.history.pushState({}, '', `?tn=${tn}`);
fetchTracking(tn);
}
});
async function fetchTracking(trackingNumber) {
const loading = document.getElementById('loading');
const results = document.getElementById('results-section');
const search = document.getElementById('search-section');
loading.style.display = 'flex';
results.style.display = 'none';
try {
const response = await fetch(`/api/track/${trackingNumber}`);
const data = await response.json();
if (!response.ok) throw new Error(data.error);
renderResults(data);
results.style.display = 'block';
} catch (error) {
results.innerHTML = `
<div class="error-message">
<h2>Unable to find tracking information</h2>
<p>Please check your tracking number and try again.</p>
</div>
`;
results.style.display = 'block';
} finally {
loading.style.display = 'none';
}
}
function renderResults(data) {
const { tracking, order } = data;
const config = STATUS_CONFIG[tracking.status] || STATUS_CONFIG.pending;
// Status banner
document.getElementById('status-banner').innerHTML = `
<div class="status-icon" style="background: ${config.color}20; color: ${config.color}">
${config.label}
</div>
<h2>${getStatusMessage(tracking.status)}</h2>
${tracking.estimatedDelivery
? `<p class="eta">Estimated delivery: ${formatDate(tracking.estimatedDelivery)}</p>`
: ''}
`;
// Order items (if available)
if (order?.items?.length) {
document.getElementById('order-items').innerHTML = `
<h3>Your Items</h3>
<div class="items-grid">
${order.items.map(item => `
<div class="item-card">
<img src="${item.image}" alt="${item.name}" />
<span>${item.name}</span>
<span class="qty">x${item.quantity}</span>
</div>
`).join('')}
</div>
`;
}
// Timeline
document.getElementById('tracking-timeline').innerHTML = `
<h3>Tracking History</h3>
<div class="timeline">
${tracking.events.map((event, i) => `
<div class="timeline-event ${i === 0 ? 'latest' : ''}">
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="event-time">${formatDateTime(event.timestamp)}</div>
<div class="event-status">${event.description}</div>
${event.location ? `<div class="event-location">${event.location}</div>` : ''}
</div>
</div>
`).join('')}
</div>
`;
}
function getStatusMessage(status) {
const messages = {
pending: 'Your order is being prepared',
picked_up: 'Your package has been picked up',
in_transit: 'Your package is on its way',
out_for_delivery: 'Your package is out for delivery today!',
delivered: 'Your package has been delivered',
exception: 'There is an issue with your delivery',
};
return messages[status] || 'Tracking your package';
}
function formatDate(dateStr) {
return new Date(dateStr).toLocaleDateString('en-US', {
weekday: 'long', month: 'long', day: 'numeric',
});
}
function formatDateTime(dateStr) {
return new Date(dateStr).toLocaleString('en-US', {
month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit',
});
}
Step 4: Styling
Make it match your brand:
:root {
--brand-primary: #4f46e5;
--brand-secondary: #818cf8;
--text-primary: #1f2937;
--text-secondary: #6b7280;
--bg-primary: #ffffff;
--bg-secondary: #f9fafb;
--border: #e5e7eb;
}
.tracking-container {
max-width: 640px;
margin: 0 auto;
padding: 2rem 1rem;
}
.status-banner {
text-align: center;
padding: 2rem;
background: var(--bg-secondary);
border-radius: 12px;
margin-bottom: 2rem;
}
.status-icon {
display: inline-block;
padding: 0.5rem 1rem;
border-radius: 9999px;
font-weight: 600;
font-size: 0.875rem;
margin-bottom: 0.75rem;
}
.timeline {
position: relative;
padding-left: 2rem;
}
.timeline::before {
content: '';
position: absolute;
left: 7px;
top: 0;
bottom: 0;
width: 2px;
background: var(--border);
}
.timeline-event {
position: relative;
padding-bottom: 1.5rem;
}
.timeline-dot {
position: absolute;
left: -2rem;
top: 4px;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--border);
border: 3px solid var(--bg-primary);
}
.timeline-event.latest .timeline-dot {
background: var(--brand-primary);
}
.event-time {
font-size: 0.8125rem;
color: var(--text-secondary);
}
.event-status {
font-weight: 500;
color: var(--text-primary);
}
.event-location {
font-size: 0.875rem;
color: var(--text-secondary);
}
Step 5: Add Engagement Elements
Maximize the value of your tracking page:
Upsell Related Products
function renderRecommendations(order) {
if (!order) return;
return `
<section class="recommendations">
<h3>You might also like</h3>
<div class="product-grid">
${getRelatedProducts(order.items).map(product => `
<a href="${product.url}" class="product-card">
<img src="${product.image}" alt="${product.name}" />
<span class="product-name">${product.name}</span>
<span class="product-price">${product.price}</span>
</a>
`).join('')}
</div>
</section>
`;
}
Collect Feedback
After delivery, ask for feedback:
if (tracking.status === 'delivered') {
resultsHTML += `
<section class="feedback-section">
<h3>How was your delivery experience?</h3>
<div class="rating-buttons">
<button onclick="submitRating(5)">Excellent</button>
<button onclick="submitRating(4)">Good</button>
<button onclick="submitRating(3)">Average</button>
<button onclick="submitRating(2)">Poor</button>
</div>
</section>
`;
}
SEO Benefits
A branded tracking page also provides SEO benefits:
- Increased time on site — Customers check tracking multiple times
- Lower bounce rate — Customers stay on your domain
- Internal linking — Link to products, blog posts, and support pages
- Brand authority — Professional tracking experience builds trust
Summary
A branded tracking page is one of the highest-ROI projects you can build. It reduces support tickets, keeps customers engaged, creates upsell opportunities, and provides a professional experience. With WhereParcel’s API providing the tracking data, you can focus entirely on the frontend experience.
Key resources:
- WhereParcel API documentation for tracking data
- Webhook setup for real-time updates
- E-commerce notifications for the full notification system