How to correctly generate product URLs in a multilingual Shopify Customer Account Extension?

Hi everyone,

I’m working on a Customer Account Extension and I’m running into an issue with generating the correct product and cart URLs when the store is multilingual.

Depending on the store configuration, Shopify may use:

  • a path prefix: mystore.com/fr

  • or a subdomain: fr.mystore.com

Inside the extension, I haven’t found any reliable way to determine:

  1. Whether the store uses subdomains or path prefixes for languages
    mystore.com/fr vs fr.mystore.com

  2. What the store’s default language/domain is
    mystore.com

  3. Any recommended or official approach to build correct product/cart URLs from a Customer Account Extension without risking broken links.

Is there a best practice, an API, or a documented solution to handle multilingual URLs properly in this context?

Thanks in advance for your help!

Hi @Celine_Lamotte,

The best way to go about retrieving Market contextualized Product URLs and Checkout URLs from a Customer Account Extension app, is to use the Storefront API specifically.

Using this, you can query the Storefront API from your Extension, and use inContext Directives to specify what Market should be used with the data that is returned by passing a language and/or country that aligns with an existing Market on the shop in the directive.

For example here’s a Storefront API query to retrieve the OnlineStoreURL for a product in a specific market based on Country and Language. This would return the OnlineStoreURL for mystore.com/fr or fr.mystore.com depending on how your domains are setup for the French Market on the store.

query getProduct @inContext(country: FR, language: FR) {
  product(id: "gid://shopify/Product/12345678910") {
    title
    id
    onlineStoreUrl
  }
}

It is similar for getting Checkout URL’s from a cart query or mutation. However the actual checkoutUrl won’t indicate the market in this case like it does for the product’s onlineStoreUrl field, but it will be the correct Market served to the customer in the actual checkout itself.

mutation cartCreate($input: CartInput) @inContext(country: FR, language: FR) {
  cartCreate(input: $input) {
    cart {
      checkoutUrl
    }
    userErrors {
      field
      message
    }
    warnings {
      code
      message
    }
  }
}

Hello, thanks a lot for your response!

However, I’m running into an issue: even though my products are properly published on the Online Store and my markets are correctly configured, onlineStoreUrl still returns null.

I even tried querying without using @inContext to make sure the problem isn’t coming from my market settings, but it still returns null.

Do you have any idea why this might happen or how to reliably get the product URL in a Customer Account Extension?

Thanks for the details. When onlineStoreUrl returns null, it often means the Customer Account Extension can’t resolve the storefront URL, even if the product is published. This can happen if the product template isn’t fully set up or if the storefront domain isn’t recognized in that context. Since this field isn’t always guaranteed in extensions, a reliable workaround is to manually build the URL using the store’s domain and product handle. If you can share you query, I can help review it.

Hello,

Sure, here is my full query and the fetch code I’m using.

const query = {
        query: `
          query getProduct($id: ID!, $country: CountryCode, $language: LanguageCode) @inContext(country: $country, language: $language) {
            product(id: $id) {
              id
              title
              handle
              vendor
              featuredImage { url altText }
              onlineStoreUrl
              variants(first: 250) {
                edges {
                  node {
                    id
                    title
                    image { url }
                    selectedOptions { name value }
                    price { amount currencyCode }
                    compareAtPrice { amount currencyCode }
                    unitPrice { amount currencyCode }
                    availableForSale
                  }
                }
              }
            }
          }
        `,
        variables: {
          id: `gid://shopify/Product/${productId}`,
          country: countryCode,
          language: languageCode
        }
      };

await fetch("shopify://storefront/api/2025-10/graphql.json", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ query })
});

Thanks

Hi @Celine_Lamotte,

Thanks for that context, can I also ask for the x-request-id from the HTTP Response Headers, and I can help look into this internally as well.

Hi @Kellan-Shopify

Here is the x-request-id you asked for:
af59543c-40e6-4eb9-99ac-d0c2387ca3ce-1764318073

Let me know if you need anything else — I can also re-run the query with a specific country/language context if that helps your investigation.

Thank you again for your assistance!

Hi @Celine_Lamotte,

Thank you for sharing that request id. I’ll be DM’ing you directly with more details on the behaviour occurring with this store specifically.

For anyone following along with this thread, if you’re experiencing the same issue with a Storefront API query where the product or the product’s onlineStoreUrl field is returning null, please be sure to check the following on your store in question.

If the product is returning null:

If the product.onlineStoreUrl is returning null:

  • Ensure that the product is published to the Online Store Channel

  • Ensure that the Online Store channel is not password protected, and the storefront is live and accessible for customers to browse.