@cw-parcelpanel/headless-react
v1.3.14
Published
ParcelPanel Headless React Component - EDD & Tracking widgets for Shopify stores with TypeScript support
Readme
ParcelPanel React Components
Professional React components for ParcelPanel's delivery and tracking widgets, specifically designed for Shopify Headless commerce solutions.
Components
ParcelPanelEDD- Estimated Delivery Date (EDD) widget for headless product pagesTrackingWidget- Order tracking widget for headless tracking pages
Why Choose ParcelPanel for Shopify Headless?
- 🏪 Shopify Native - Built specifically for Shopify's ecosystem
- 🚀 Headless Ready - Perfect for custom storefronts and mobile apps
- 📦 Enterprise Grade - Trusted by thousands of Shopify merchants
- 🔧 Developer Friendly - Clean APIs and comprehensive TypeScript support
Features
- 🚀 Easy Integration - Simple React component with minimal setup
- 📱 Responsive Design - Automatically adapts to different screen sizes
- 🌍 Multi-language Support - Built-in internationalization support
- 🎨 Customizable Styling - Flexible styling options and CSS customization
- ⚡ Performance Optimized - Lightweight and efficient rendering
- 🔧 TypeScript Support - Full TypeScript definitions included
Installation
For Shopify Headless projects (Next.js, Remix, Hydrogen, etc.):
npm install @cw-parcelpanel/headless-reactNote: This package is designed for Shopify headless commerce solutions. If you're using traditional Shopify themes, please use our theme-based solution instead.
Quick Start
EDD Widget (Headless Product Pages)
Perfect for Next.js, Remix, Hydrogen, or any React-based Shopify headless storefront:
import React from 'react';
import { ParcelPanelEDD } from '@cw-parcelpanel/headless-react';
function ProductPage({ product }) {
return (
<div>
<h1>{product.title}</h1>
<div className="product-details">
{/* Your product content */}
</div>
{/* ParcelPanel EDD Widget */}
<ParcelPanelEDD
shopDomain="your-shop.myshopify.com"
productId={product.id}
locale="en"
customer={{
country_code: "US", // From user's location or checkout
province_code: "CA"
}}
onLoaded={(sdk) => console.log('EDD widget loaded:', sdk)}
onError={(error) => console.error('EDD error:', error)}
/>
</div>
);
}Tracking Widget (Headless Tracking Pages)
Ideal for custom tracking pages in headless storefronts:
import React from 'react';
import { TrackingWidget } from '@cw-parcelpanel/headless-react';
function TrackingPage() {
return (
<div>
<h1>Track Your Order</h1>
<p>Enter your tracking information below:</p>
{/* ParcelPanel Tracking Widget */}
<TrackingWidget
shopDomain="your-shop.myshopify.com"
locale="en"
className="tracking-container"
onLoaded={() => console.log('Tracking widget loaded')}
onError={(error) => console.error('Tracking error:', error)}
/>
</div>
);
}API Reference
ParcelPanelEDD Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| shopDomain | string | ✅ | Your Shopify shop domain (e.g., "example.myshopify.com") |
| productId | number \| string | ❌ | Product ID to display delivery estimates for |
| locale | string | ❌ | Language locale (e.g., "en", "zh-CN", "es") |
| className | string | ❌ | CSS class name for styling |
| style | React.CSSProperties | ❌ | Inline styles object |
| isPreview | boolean | ❌ | Enable preview mode for testing |
| apiEndpoint | 'test' \| 'prod' \| string | ❌ | API endpoint configuration |
| customer | Customer | ❌ | Customer location information (country and province codes) |
| settings | WidgetSettings | ❌ | Widget styling configuration |
| selectorOptions | SelectorOptions | ❌ | Address selector configuration |
| onLoaded | (sdk: SDK) => void | ❌ | Callback when component loads successfully |
| onError | (error: Error) => void | ❌ | Callback when an error occurs |
| onLocationChange | (location: Location) => void | ❌ | Callback when address/location changes |
Ref Methods (Optional)
Use useRef to access component methods when you need programmatic control. This is optional - the component works perfectly without refs for most use cases:
import React, { useRef } from 'react';
import { ParcelPanelEDD, ParcelPanelEDDRef } from '@cw-parcelpanel/headless-react';
function ProductPage() {
const eddRef = useRef<ParcelPanelEDDRef>(null);
const handleUpdateProduct = () => {
// Only needed if you want to update product programmatically
eddRef.current?.setProduct({
id: 789,
available: true,
variants: [...]
});
};
return (
<ParcelPanelEDD
ref={eddRef}
shopDomain="your-shop.myshopify.com"
productId={123}
/>
);
}Note: For most use cases, you can simply pass props to the component and it will handle everything automatically. Refs are only needed for advanced programmatic control.
Available ref methods:
| Method | Parameters | Description |
|--------|------------|-------------|
| getSDK() | - | Get the underlying SDK instance |
| setProduct(info) | SDKProductInfo | Update product information |
| setCustomer(customer) | Customer | Update customer location information |
| setSettings(settings) | WidgetSettings | Update widget styling |
| updateConfig(config) | Partial<SDKConfig> | Update SDK configuration |
| destroy() | - | Clean up and destroy the component |
Type Definitions
Customer
interface Customer {
country_code: string; // Country code (ISO 3166-1 alpha-2), e.g., "US", "CN"
province_code: string; // Province/state code, e.g., "CA", "NY"
}WidgetSettings
interface WidgetSettings {
corner_radius?: number; // Border radius in pixels
margin_top?: number; // Top margin in pixels
margin_bottom?: number; // Bottom margin in pixels
padding_x?: number; // Horizontal padding in pixels
padding_y?: number; // Vertical padding in pixels
}SelectorOptions
interface SelectorOptions {
country?: string; // Default country code
province?: string; // Default province/state code
settings?: string; // JSON string of additional settings
}Advanced Usage
Custom Styling
<ParcelPanelEDD
shopDomain="your-shop.myshopify.com"
productId={123}
className="my-edd-widget"
style={{
border: '1px solid #e1e5e9',
borderRadius: '8px',
padding: '16px'
}}
settings={{
corner_radius: 8,
margin_top: 10,
margin_bottom: 10,
padding_x: 16,
padding_y: 12
}}
/>Handling Events
<ParcelPanelEDD
shopDomain="your-shop.myshopify.com"
productId={123}
customer={{ country_code: "US", province_code: "CA" }}
onLoaded={(sdk) => {
console.log('Widget loaded successfully');
// Access SDK methods
console.log('Current config:', sdk.getConfig());
}}
onError={(error) => {
console.error('Failed to load widget:', error);
// Handle error (show fallback UI, report to analytics, etc.)
}}
onLocationChange={(location) => {
console.log('Selected location:', location);
// Update shipping calculations, etc.
}}
/>Dynamic Product Updates
function ProductVariantSelector() {
const eddRef = useRef<ParcelPanelEDDRef>(null);
const [selectedVariant, setSelectedVariant] = useState(null);
useEffect(() => {
if (selectedVariant && eddRef.current) {
// Update the EDD widget when variant changes
eddRef.current.setProduct({
id: selectedVariant.product_id,
available: selectedVariant.available,
variants: [{
id: selectedVariant.id,
available: selectedVariant.available,
requires_shipping: selectedVariant.requires_shipping
}]
});
}
}, [selectedVariant]);
return (
<div>
{/* Variant selector UI */}
<ParcelPanelEDD
ref={eddRef}
shopDomain="your-shop.myshopify.com"
productId={123}
/>
</div>
);
}TrackingWidget Component
The TrackingWidget component provides order tracking functionality, allowing customers to track their orders directly within your application.
Basic Usage
import React from 'react';
import { TrackingWidget } from '@cw-parcelpanel/headless-react';
function TrackingPage() {
return (
<div>
<h1>Track Your Order</h1>
<TrackingWidget
shopDomain="your-shop.myshopify.com"
locale="en"
onLoaded={() => console.log('Tracking widget loaded')}
onError={(error) => console.error('Tracking error:', error)}
/>
</div>
);
}TrackingWidget Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| shopDomain | string | ✅ | Your Shopify shop domain (e.g., "your-shop.myshopify.com") |
| locale | string | ❌ | Language locale (e.g., "en", "zh-CN", "ja"). Defaults to "en" |
| className | string | ❌ | CSS class name for styling |
| style | React.CSSProperties | ❌ | Inline styles object |
| onLoaded | () => void | ❌ | Callback when tracking widget loads successfully |
| onError | (error: Error) => void | ❌ | Callback when an error occurs |
Multi-language Support
// English tracking page
<TrackingWidget shopDomain="your-shop.myshopify.com" locale="en" />
// Chinese tracking page
<TrackingWidget shopDomain="your-shop.myshopify.com" locale="zh-CN" />
// Japanese tracking page
<TrackingWidget shopDomain="your-shop.myshopify.com" locale="ja" />Custom Styling
<TrackingWidget
shopDomain="your-shop.myshopify.com"
className="custom-tracking-widget"
style={{
minHeight: '400px',
backgroundColor: '#f5f5f5',
padding: '20px',
borderRadius: '8px'
}}
/>Error Handling
function TrackingPage() {
const handleTrackingError = (error: Error) => {
console.error('Tracking widget error:', error);
// You can show a user-friendly error message
alert('Failed to load tracking information. Please try again.');
};
return (
<TrackingWidget
shopDomain="your-shop.myshopify.com"
onError={handleTrackingError}
/>
);
}Automatic Variant Detection
The component automatically detects variant changes from:
- URL Parameters -
?variant=123456in the URL - Shopify Form Inputs - Product form variant selectors
- Manual Updates - Via ref methods or prop changes
No need to manually pass variantId - the widget handles variant detection automatically!
Shopify Headless Integration
Next.js Integration
// pages/products/[handle].tsx or app/products/[handle]/page.tsx
import { ParcelPanelEDD } from '@cw-parcelpanel/headless-react';
export default function ProductPage({ product }) {
return (
<main>
{/* Your product layout */}
<ParcelPanelEDD
shopDomain={process.env.NEXT_PUBLIC_SHOPIFY_SHOP_DOMAIN}
productId={product.id}
locale={product.locale}
apiEndpoint={process.env.NODE_ENV === 'production' ? 'prod' : 'test'}
/>
</main>
);
}Hydrogen Integration
// app/routes/products.$handle.tsx
import { ParcelPanelEDD } from '@cw-parcelpanel/headless-react';
export default function Product() {
const { product } = useLoaderData();
return (
<div>
{/* Your Hydrogen product template */}
<ParcelPanelEDD
shopDomain={shop.primaryDomain.url.replace('https://', '')}
productId={product.id}
locale={request.locale}
/>
</div>
);
}Remix Integration
// app/routes/products.$handle.tsx
import { ParcelPanelEDD } from '@cw-parcelpanel/headless-react';
export default function ProductRoute() {
const { product } = useLoaderData();
return (
<div>
{/* Your Remix product template */}
<ParcelPanelEDD
shopDomain={ENV.SHOPIFY_SHOP_DOMAIN}
productId={product.id}
/>
</div>
);
}Environment Configuration
// Development
<ParcelPanelEDD
shopDomain="your-shop.myshopify.com"
productId={123}
apiEndpoint="test"
isPreview={true}
/>
// Production
<ParcelPanelEDD
shopDomain="your-shop.myshopify.com"
productId={123}
apiEndpoint="prod"
/>Internationalization
The component supports multiple languages:
// English
<ParcelPanelEDD shopDomain="your-shop.myshopify.com" locale="en" />
// Chinese (Simplified)
<ParcelPanelEDD shopDomain="your-shop.myshopify.com" locale="zh-CN" />
// Spanish
<ParcelPanelEDD shopDomain="your-shop.myshopify.com" locale="es" />
// French
<ParcelPanelEDD shopDomain="your-shop.myshopify.com" locale="fr" />Troubleshooting
Common Issues
Widget not displaying
- Ensure
shopDomainis correct and accessible - Check that the product exists and is available
- Verify API endpoint configuration
- Ensure
TypeScript errors
- Make sure you're importing types correctly
- Check that all required props are provided
Styling issues
- Use browser dev tools to inspect the widget
- Ensure CSS specificity is sufficient
- Check for conflicting styles
