Shipping Calculator Block
The Shipping Calculator is a theme app extension block that lets shoppers preview shipping rates from any storefront page (product detail page or cart page) before they reach checkout. The block reuses your active Shipping Rate script — no new script type or backend code is required.
This block calls the same /task/{id} endpoint that Shopify's Carrier Service uses at checkout, but goes through the app proxy. Your existing shipping_rate script handles both flows.
How It Works
Merchant adds one of two blocks to a theme section in the theme editor:
Shipping Calculator (Product) — for product detail pages
Shipping Calculator (Cart) — for the cart page
Shopper enters address fields (postal code is required, other fields are toggleable per block).
The block sends a
POSTrequest to/apps/{proxy-subpath}/task/{script_id}containing the destination address, line items, currency, locale, and logged-in customer info.Your shipping_rate script receives the payload as
request.body.rate(identical shape to the checkout flow) and returns rates.The block parses the response and renders each rate inline.
Data flow
Shopper fills form → Block builds payload → POST /apps/{proxy}/task/{id}
↓
DataJet runs your shipping_rate script with request.body.rate
↓
Script returns { "rates": [...] }
↓
Block renders rates list to shopperSetup
Step 1: Activate a Shipping Rate Script
You must have an active Shipping Rate script in DataJet before the calculator can return rates. See Shipping Rate for setup. Note its script ID (or handle) — you'll paste it into the block settings.
Shopify allows only one active shipping_rate script per merchant. If you don't have one, you'll get an empty response.
Step 2: Add the Block to Your Theme
In Shopify admin, open Online Store > Themes and click Customize on the active theme.
Navigate to a product page (for the Product block) or the cart page (for the Cart block).
In a section that accepts blocks, click Add block and pick:
Shipping Calculator (Product) on a product section, or
Shipping Calculator (Cart) on the cart section.
Paste the script ID from Step 1 into the Shipping rate script ID field.
Configure which address fields to show (see below) and save.
Block Settings
Both blocks expose the same settings:
script_id
text
required
Script ID (Mongo ObjectId) or handle of your active shipping_rate script.
title
text
Calculate shipping
Heading rendered above the form. Leave blank to hide.
button_label
text
Calculate
Submit button text.
show_country
checkbox
true
Render a country input.
show_province
checkbox
false
Render a province / state input.
show_city
checkbox
true
Render a city input.
show_street
checkbox
false
Render a street (address1) input.
show_address2
checkbox
false
Render an address line 2 input.
prefill_customer_address
checkbox
true
When the shopper is logged in, pre-fill the inputs from customer.default_address.
The postal/ZIP code input is always rendered.
Request Payload
The block POSTs JSON in the same shape your shipping_rate script already understands:
Notes:
destination— only includes keys whose form inputs are present and non-empty. Postal code is always present; the others are gated by block settings.items— for the Product block, contains a single item built fromproduct.selected_or_first_available_variantat render time. For the Cart block, all line items are fetched from/cart.jsat submit time.origin— not included. The storefront does not know the merchant's warehouse address. If your script needsrequest.body.rate.origin, hardcode it or fetch it from a DataJet variable.customer—nullwhen the shopper is logged out. Otherwise containsid,email,first_name,last_name,tags._datajet_source— always"storefront_calculator". Use this to distinguish calls from the block from real Shopify Carrier Service calls._datajet_mode—"product"or"cart".
Distinguishing block calls in your script
If you want different logic when the block calls vs. when Shopify's Carrier Service calls at checkout:
Response Format
The block expects the same response your shipping_rate script already returns:
total_price is in cents (matching Shopify's convention). The block formats it via Intl.NumberFormat using the rate's currency. If description is present, it's rendered under the rate name.
States the block renders:
rates array has entries
List of rates, one per row, with name + formatted price + optional description
rates is empty
Empty-state copy ("No shipping options available for this address.")
Non-2xx response or fetch error
Error copy ("Could not calculate shipping. Please try again.")
Example: Different Rates for Block vs. Checkout
A typical pattern is to return broad estimates from the block but precise per-carrier rates at checkout:
Logged-In Customer Data
When the shopper is logged in, the block renders customer info server-side via Liquid's customer global and includes it in request.body.rate.customer. This means:
customer.tagsis available without needing the Shipping Rate Context checkout extension. The block delivers tags directly in the payload.If
prefill_customer_addressis enabled andcustomer.default_addressexists, the form inputs are pre-populated.
Limitations
Variant changes on PDP — the Product block snapshots the current variant at server-render time. If the shopper switches variants on the PDP, the payload still references the originally rendered variant until the page reloads.
One block per page — multiple instances on the same page work, but each makes its own request. There is no shared state.
No origin — the block does not provide
request.body.rate.origin. Scripts that depend on it must source it elsewhere.App proxy only — the request goes through the Shopify app proxy. Make sure your app proxy is configured in
shopify.app.tomland the subpath in the block's JS asset matches.
Troubleshooting
Form submits but no rates appear
Confirm a shipping_rate script is active in DataJet.
Check the script ID in block settings matches the active script.
Inspect the script's run logs in DataJet — look for the request body and any errors.
Error message appears immediately
Open the browser Network tab and inspect the
POST /apps/{proxy}/task/{id}response.404usually means the app proxy subpath in the block's JS does not matchshopify.app.toml's[app_proxy].subpath.5xxmeans the script threw — check DataJet's script logs.
Customer fields don't pre-fill
Pre-fill only works when the shopper is logged in and has a default address saved on their Shopify customer profile.
Confirm
prefill_customer_addressis checked in block settings.
Cart block sends wrong line items
The cart is fetched from
/cart.jsat submit time. If items look stale, force a hard reload to clear any cached cart state.
Last updated