How and where are you trying to read these variables? You shouldnât be reading them directly. I believe that shopify prefers you to use either âReact-style Hooks API (@shopify/checkout-ui-extensions-react)â or âWeb components + shopify APIs (@shopify/checkout-ui-extensions)â Even if you are using the right read method, the when is also important as you need to read it after it has been entered by the customer. Being logged in with Gmail doesnât guarantee that the information that you want exists in the required field yet. If you try and read the shipping address before the customer has entered one, or used a saved address, then the field will be empty at that point. If you use Shop pay, or another accelerated checkout method, they can skip or pre-fill some of these in ways that will come up as undefined. Maybe try looking for shopify.checkout.billingAddress instead ofshopify.billingAddress. It may exist there, even if itâs not defined at the top level.
I am creating a checkout UI extension using preact (@shopify/ui-extensions/preact). In my code, shopify.shippingAddress exists, but its value is still undefined (please check the image below).
Also, the code is used is below. And I am not using Shop App.
Regarding your question: I am trying to read these variables directly.
Sorry for taking so long to get back to you. What you are seeing is actually completely normal behaviour for the new Checkout UI global API. Hereâs the deal: shopify.shippingAddress is essentially a reactive container (similar to how state works in React or @preact/signals). Because of that, the object itself will always exist, but its actual .value is usually going to be undefined when the extension first loads. There are a few reasons why this happens. 1. Timing: Youâre doing a console.log right when the component mounts. At that exact split second, the buyer usually hasnât entered their address yet, so itâs naturally undefined. 2. Extension Target: If youâre putting this extension on an early checkout step (like contact info) instead of a shipping-specific step, the address hasnât been collected yet. 3. Cart Contents: If you happen to be testing with a digital product, the checkout wonât collect a shipping address at all. How to fix it: Since shippingAddress updates dynamically as the user types or moves through the checkout, you canât just read it once on load. You need to subscribe to its changes.
Since youâre using Preact, hereâs a very common, clean pattern. You can use Preact signals combined with Shopifyâs .subscribe() method to safely grab that province code and keep it updated in real-time:
import â@shopify/ui-extensions/preactâ;
import { render } from âpreactâ;
import { signal } from â@preact/signalsâ;
function Extension() {
// Set the initial state (it will likely be null at first!)
const stateCode = signal(shopify.shippingAddress?.value?.provinceCode ?? null);
// Subscribe to changes so the UI updates automatically as the buyer types their address
shopify.shippingAddress?.subscribe((nextAddress) => {
stateCode.value = nextAddress?.provinceCode ?? null;
});
if (!shopify.instructions.value.metafields.canSetCartMetafields) {
return (
Province Code:
{/* This will update from âNot entered yetâ to the actual code as they type */}
{stateCode.value ?? âNot entered yetâ}
);
}
A couple of other things to check. 1 Your .toml file. Make sure that your target in shopify.extension.toml is set to something delivery-related, like purchase.checkout.delivery-address.render-before. If you use a generic block on the very first page of checkout, the shipping info might just not exist yet. 2 Donât trust console.log for this. Because the shopify object is reactive, your initial console.log will just print the empty starting state. It wonât magically update in your dev tools when the user types. Rendering the value directly to the screen (like in the code above) is the best way to actually see it working. Documen ts for you to refer to:
Hi, sorry for the late reply and I really appreciate you taking the time to write such a detailed response. Just wanted to close the loop and let you know that the issue is now resolved.