Need navigation history replace OR back-arrow override on <s-page>

Hi all,

I’m building a POS UI extension with a multi-step flow and hitting a navigation limitation.

The flow:
Home → Screen1 → Screen2 → Screen3

After the user completes the workflow on Screen3, Screen1 and Screen2 represent stale state.
But when they tap the native < back arrow, they have to walk back through all of them to reach Home.

Is there any way to fix this today? I’ve looked through the Navigation API and props and couldn’t find a clean solution.

If not, either of these would solve the use case:

  1. A way to replace or clean up the navigation history (e.g., a replace option on navigation.navigate(), or a method to reset/trim the history stack).
  2. A way to override or hide the back button on (e.g., an onBack callback or hideBackButton prop).

Is either on the roadmap?

Thanks!

@Tiago_Ramalho Hey there! What API version are you on? On Screen1 and Screen2 you should be able to listen to navigation history changes with addEventListener(‘currententrychange‘)

Hi @JS_Goupil.

I’m not sure if @Tiago_Ramalho is experiencing the same issue, but I noticed that the global state can no longer be shared between pages when using the new navigation API. Please see The new POS navigation API is unusable - #4 by Galmis for reproduction steps. In the ticket, I added more symptoms of (what I think is) the same root cause.

@Galmis yeah in pre 2025-10, your extension and all of its screens was mounted within a single JavaScript worker. Now, when you navigate, each screen is mounted in its own instance. This was unfortunately a trade off we had to make to be aligned with the Web Standard Navigation API. However, you should still be able to subscribe to the navigation current entry and pass data in there. We are working on a better solution for you to be able to subscribe to changes in other screens, cross instance (worker). ie your Screen 3 would be able to subscribe to changes that happen in Screen 1.

Thanks for confirming @JS_Goupil. This really helps!

in pre 2025-10, your extension and all of its screens was mounted within a single JavaScript worker. Now, when you navigate, each screen is mounted in its own instance. This was unfortunately a trade off we had to make to be aligned with the Web Standard Navigation API.

I like the steer towards web standards and tech-framework agnosticism - textbook approach for micro frontends architecture. However, I wish the new behaviour were documented. It’s a significant change and deserves at least a few sentences in the docs, imo. While the docs say that the navigation API “follows web platform standards”, it’s not clear what that entails in the native mobile app.

However, you should still be able to subscribe to the navigation current entry and pass data in there

Yeah, I got the previous example to work by storing the action result via the storage API (no option to pass state in the back method) and then rehydrating the state in the current entry event handler. Is this the recommended approach?

The stale page issue highlighted in The new POS navigation API is unusable - #6 by Galmis comment was caused by shopify.cart.current.value accessed before the page is rendered (cart update action + back() are required, please see the snippet in the linked comment). For example,

This fails

const cartValue = shopify.cart.current.value
return <s-page heading="Test"><SomeContent /></s-page>

This works

return <s-page heading="Test"><SomeContent /></s-page>

function SomeContent() {
  const cartValue = shopify.cart.current.value
  // ... render something - omitted for brevity
}

Is this expected?

We are working on a better solution for you to be able to subscribe to changes in other screens, cross instance (worker). ie your Screen 3 would be able to subscribe to changes that happen in Screen 1.

This sounds complex. Would it be possible to have an option to opt out of the default behaviour, override the Back/Close button, and listen for back navigation intents (back/close button click and the back gesture)? The web navigation API has the intercept method “to implement same-document (SPA) navigation behavior when a navigation occurs“. This would enable global state sharing while still adhering to web standards. Now, for shared data (e.g., app settings), each page needs to fetch it or retrieve it from the storage API if it exists.

Cheers!

@Galmis so sorry for the delay in responding. I’m looking at your example and I’m not sure why one works and one doesn’t, I’ll have a look. Just wanna confirm, are you importing the preact bindings (`import ‘@shopify/ui-extensions/preact‘`) for the cart.current.value?

As for your frustration about the lack of docs around the navigation behaviour - noted. I’ll get on this.

Would it be possible to have an option to opt out of the default behaviour, override the Back/Close button, and listen for back navigation intents (back/close button click and the back gesture)?

Solid feedback, I think this would be a great feature.

No problem @JS_Goupil.

I’m looking at your example and I’m not sure why one works and one doesn’t, I’ll have a look. Just wanna confirm, are you importing the preact bindings (`import ‘@shopify/ui-extensions/preact‘`) for the cart.current.value?

Yes, please see the full snippet with the import at the top

import "@shopify/ui-extensions/preact";
import { render } from "preact";

export default function renderExtension() {
  render(<ExampleThree />, document.body);
}

function ExampleThree() {
  const lines = shopify.cart.current.value.lineItems;

  console.log(
    `\n ${new Date().toISOString()} ExampleThree, navigation.currentEntry.url -`,
    navigation.currentEntry.url
  );

  if (navigation.currentEntry.url === "/test") {
    return (
      <s-page heading="Test page">
        <s-button
          onClick={async () => {
            // NOTE:
            // - Ensure at least one line item is in the cart
            // - Ensure the first line item doesn't already have "test":"1" property set
            await shopify.cart.bulkAddLineItemProperties([
              {
                lineItemUuid: lines[0].uuid,
                properties: {
                  test: "1",
                },
              },
            ]);
            navigation.back();
          }}
        >
          Bulk add line item properties & go back
        </s-button>
      </s-page>
    );
  }

  return (
    <s-page heading="Initial page">
      <s-button onClick={() => navigation.navigate("/test")}>
        Go to test page
      </s-button>
    </s-page>
  );
}