Skip to Content

Setting Up International Shipping Zones by Country and State

International shipping is one of the most complex parts of e-commerce. Every carrier, marketplace, and fulfillment provider structures their rates differently, but they all share one thing in common: they need standardized country and subdivision codes to define shipping zones.

This guide shows how to build a shipping zone system using ISO 3166 codes, implement zone-based pricing, and integrate it into your checkout flow with @koshmoney/countries.

What Are Shipping Zones?

A shipping zone is a grouping of countries or regions that share the same shipping rate and delivery timeline. For example:

  • Domestic — your home country
  • North America — US, Canada, Mexico
  • EU — European Union member states
  • Rest of World — everything else

Carriers like FedEx, UPS, and DHL use their own zone numbering systems, but the underlying concept is the same: group destinations by geographic proximity and charge accordingly.

Defining Zones with Country Codes

Basic Zone Configuration

import { country } from '@koshmoney/countries'; import { membership } from '@koshmoney/countries/membership'; import { geography } from '@koshmoney/countries/geography'; interface ShippingZone { id: string; name: string; countries: string[]; baseRate: number; perKgRate: number; estimatedDays: { min: number; max: number }; } function defineZones(homeCountry: string): ShippingZone[] { const euCountries = membership.getMembers('EU'); const northAmerica = ['US', 'CA', 'MX']; const asiaCountries = geography.countriesInContinent('Asia'); return [ { id: 'domestic', name: 'Domestic', countries: [homeCountry], baseRate: 5.99, perKgRate: 0.50, estimatedDays: { min: 1, max: 3 }, }, { id: 'north-america', name: 'North America', countries: northAmerica.filter(c => c !== homeCountry), baseRate: 12.99, perKgRate: 2.00, estimatedDays: { min: 3, max: 7 }, }, { id: 'eu', name: 'European Union', countries: euCountries.filter(c => c !== homeCountry), baseRate: 14.99, perKgRate: 2.50, estimatedDays: { min: 5, max: 10 }, }, { id: 'asia', name: 'Asia-Pacific', countries: asiaCountries, baseRate: 19.99, perKgRate: 3.50, estimatedDays: { min: 7, max: 14 }, }, { id: 'rest-of-world', name: 'Rest of World', countries: [], // Catch-all baseRate: 24.99, perKgRate: 4.00, estimatedDays: { min: 10, max: 21 }, }, ]; }

Finding the Zone for a Destination

function getShippingZone( zones: ShippingZone[], destinationCountry: string ): ShippingZone { // Check specific zones first for (const zone of zones) { if (zone.countries.includes(destinationCountry)) { return zone; } } // Fall back to rest-of-world return zones.find(z => z.id === 'rest-of-world')!; }

Calculating Shipping Cost

interface ShippingQuote { zone: string; cost: number; estimatedDays: { min: number; max: number }; } function calculateShipping( zones: ShippingZone[], destinationCountry: string, weightKg: number ): ShippingQuote { const zone = getShippingZone(zones, destinationCountry); return { zone: zone.name, cost: Math.round((zone.baseRate + zone.perKgRate * weightKg) * 100) / 100, estimatedDays: zone.estimatedDays, }; } const zones = defineZones('US'); calculateShipping(zones, 'CA', 2.5); // { zone: 'North America', cost: 17.99, estimatedDays: { min: 3, max: 7 } } calculateShipping(zones, 'DE', 1.0); // { zone: 'European Union', cost: 17.49, estimatedDays: { min: 5, max: 10 } } calculateShipping(zones, 'AU', 3.0); // { zone: 'Rest of World', cost: 36.99, estimatedDays: { min: 10, max: 21 } }

State-Level Shipping Zones

For large countries, shipping rates vary by state or province. This is especially important for domestic shipping in the US, Canada, and Australia.

US Domestic Zones by Region

import { subdivision } from '@koshmoney/countries'; type USRegion = 'west' | 'midwest' | 'south' | 'northeast' | 'non-contiguous'; const US_REGIONS: Record<string, USRegion> = { // West WA: 'west', OR: 'west', CA: 'west', NV: 'west', ID: 'west', MT: 'west', WY: 'west', CO: 'west', UT: 'west', AZ: 'west', NM: 'west', // Midwest ND: 'midwest', SD: 'midwest', NE: 'midwest', KS: 'midwest', MN: 'midwest', IA: 'midwest', MO: 'midwest', WI: 'midwest', IL: 'midwest', MI: 'midwest', IN: 'midwest', OH: 'midwest', // South TX: 'south', OK: 'south', AR: 'south', LA: 'south', MS: 'south', AL: 'south', TN: 'south', KY: 'south', WV: 'south', VA: 'south', NC: 'south', SC: 'south', GA: 'south', FL: 'south', DE: 'south', MD: 'south', DC: 'south', // Northeast PA: 'northeast', NJ: 'northeast', NY: 'northeast', CT: 'northeast', RI: 'northeast', MA: 'northeast', VT: 'northeast', NH: 'northeast', ME: 'northeast', // Non-contiguous AK: 'non-contiguous', HI: 'non-contiguous', }; const REGION_SURCHARGES: Record<USRegion, number> = { west: 0, midwest: 0, south: 0, northeast: 0, 'non-contiguous': 15.00, // Alaska/Hawaii surcharge }; function getDomesticSurcharge(stateCode: string): number { if (!subdivision.isValidRegion('US', stateCode)) { throw new Error(`Invalid US state code: ${stateCode}`); } const region = US_REGIONS[stateCode] ?? 'south'; return REGION_SURCHARGES[region]; }

Distance-Based Zone Calculation

interface DomesticZone { zone: number; // 1-8 (like USPS zones) surcharge: number; } // Simplified zone calculation based on origin and destination regions function getUSPSZone(originRegion: USRegion, destRegion: USRegion): number { if (originRegion === destRegion) return 1; const ZONE_MATRIX: Record<string, number> = { 'west-midwest': 4, 'west-south': 5, 'west-northeast': 6, 'midwest-south': 3, 'midwest-northeast': 3, 'south-northeast': 4, }; const key = [originRegion, destRegion].sort().join('-'); return ZONE_MATRIX[key] ?? 7; }

Restricted Destinations

Some countries or regions cannot receive shipments due to sanctions, embargoes, or carrier restrictions. Build this into your zone system:

import { country } from '@koshmoney/countries'; const RESTRICTED_COUNTRIES = new Set([ 'KP', // North Korea 'CU', // Cuba (US restriction) 'IR', // Iran 'SY', // Syria ]); function canShipTo(countryCode: string): { allowed: boolean; reason?: string } { if (!country.isValid(countryCode)) { return { allowed: false, reason: 'Invalid country code' }; } if (RESTRICTED_COUNTRIES.has(countryCode)) { return { allowed: false, reason: 'Shipping restricted to this destination' }; } return { allowed: true }; }

Checkout Integration

Address Validation at Checkout

import { country, subdivision, postalCode } from '@koshmoney/countries'; interface ShippingAddress { countryCode: string; stateCode?: string; postalCodeValue?: string; city: string; line1: string; } interface ValidationResult { valid: boolean; errors: string[]; } function validateShippingAddress(address: ShippingAddress): ValidationResult { const errors: string[] = []; // Validate country if (!country.isValid(address.countryCode)) { errors.push('Invalid country code'); return { valid: false, errors }; } // Validate state/province if provided if (address.stateCode) { if (!subdivision.isValidRegion(address.countryCode, address.stateCode)) { errors.push('Invalid state/province code'); } } else if (subdivision.hasSubdivisions(address.countryCode)) { // Require state for countries that have subdivisions errors.push('State/province is required'); } // Validate postal code if the country uses them if (address.postalCodeValue) { if (!postalCode.isValid(address.countryCode, address.postalCodeValue)) { const format = postalCode.getFormat(address.countryCode); errors.push(`Invalid ${postalCode.getName(address.countryCode) ?? 'postal code'}. Expected format: ${format}`); } } else if (postalCode.hasPostalCode(address.countryCode)) { errors.push(`${postalCode.getName(address.countryCode) ?? 'Postal code'} is required`); } return { valid: errors.length === 0, errors }; }

Dynamic Form Fields Based on Country

Different countries require different address fields. Use country data to adapt your checkout form:

import { subdivision, postalCode } from '@koshmoney/countries'; interface AddressFormConfig { showState: boolean; stateLabel: string; stateOptions: Array<{ value: string; label: string }>; showPostalCode: boolean; postalCodeLabel: string; postalCodePlaceholder: string; } function getAddressFormConfig(countryCode: string): AddressFormConfig { const hasSubs = subdivision.hasSubdivisions(countryCode); const subs = hasSubs ? subdivision.forCountry(countryCode) : []; const hasPostal = postalCode.hasPostalCode(countryCode); // Determine label for state field const stateType = subs[0]?.type ?? 'State'; const stateLabel = stateType === 'Province' ? 'Province' : stateType === 'Territory' ? 'Territory' : stateType === 'Region' ? 'Region' : 'State'; return { showState: hasSubs, stateLabel, stateOptions: subs.map(s => ({ value: s.regionCode, label: s.name })), showPostalCode: hasPostal, postalCodeLabel: postalCode.getName(countryCode) ?? 'Postal Code', postalCodePlaceholder: postalCode.getFormat(countryCode) ?? '', }; } getAddressFormConfig('US'); // { showState: true, stateLabel: 'State', stateOptions: [...50 states], // showPostalCode: true, postalCodeLabel: 'ZIP Code', postalCodePlaceholder: 'NNNNN or NNNNN-NNNN' } getAddressFormConfig('HK'); // { showState: true, stateLabel: 'District', stateOptions: [...], // showPostalCode: false, postalCodeLabel: 'Postal Code', postalCodePlaceholder: '' }

Free Shipping Thresholds by Zone

const FREE_SHIPPING_THRESHOLDS: Record<string, number> = { domestic: 35, 'north-america': 75, eu: 100, asia: 150, 'rest-of-world': Infinity, // No free shipping }; function getShippingCost( zones: ShippingZone[], destinationCountry: string, weightKg: number, orderTotal: number ): ShippingQuote { const zone = getShippingZone(zones, destinationCountry); const threshold = FREE_SHIPPING_THRESHOLDS[zone.id] ?? Infinity; if (orderTotal >= threshold) { return { zone: zone.name, cost: 0, estimatedDays: zone.estimatedDays, }; } return { zone: zone.name, cost: Math.round((zone.baseRate + zone.perKgRate * weightKg) * 100) / 100, estimatedDays: zone.estimatedDays, }; }

Summary

  • Define shipping zones using ISO 3166 country codes for consistency across carriers and platforms
  • Use @koshmoney/countries for country validation, subdivision lookups, and geographic groupings
  • Handle state-level zones for large countries (US, CA, AU) where domestic rates vary by region
  • Build restricted destination checks into your zone system
  • Adapt checkout form fields dynamically based on the selected country