Bug: App Bridge <s-app-window> stuck in infinite spinner on cancelled browser reload

Hey Shopify team,

I’m running a Nuxt 4 app using the App Bridge web components. We are seeing a critical UI trap when using the data-save-bar attribute inside an <s-app-window>.

The Issue:

  1. A merchant makes a change in a form (triggering the save bar).

  2. They accidentally hit the browser’s refresh button.

  3. The browser shows its native “Leave site? Changes you made may not be saved” prompt.

  4. The merchant clicks Cancel.

At this point, the <s-app-window> hides the app and overlays a permanent <ui-spinner>. Because the page reload was cancelled, the app is still running, but the Shopify wrapper never removes the spinner. The merchant is locked out of the interface and forced to do a hard refresh anyway.

Our Basic Code Setup:

HTML

<script setup lang="ts">
definePageMeta({ layout: 'window' })
useHead({ title: 'Product Editor' })
</script>

<template>
  <s-page heading="Product editor">
    <form id="settings-form" data-save-bar>
      <s-text-field label="Product title" name="productTitle"></s-text-field>
      <s-text-area label="Description" name="description"></s-text-area>
    </form>
  </s-page>
</template>

(Called via <s-app-window id="product-editor-window" src="/product-editor"></s-app-window>)

Is this a known bug with the web components, and is there a fix on the way?

Thanks,

Kamran

@Alan_G @Liam-Shopify could anyone of you please take a look at this issue when you have a chance?

We’re ready to launch our app, and this is the only remaining blocker. Any help or update would be greatly appreciated.
Thanks!

Hey @Kamran_Khan - thanks for the detailed repro here. Happy to help with this, but just wanted to note, it is generally against the community’s rules to tag specific people without them already being engaged in the conversation with you (more info on that here), but no worries on this one.

That said, this does look like an App Bridge s-app-window / save bar issue rather than an issue with your basic setup from what I can tell. The expected behaviour is that data-save-bar protects unsaved changes before exit, but in this reload-cancel flow the window appears to stay in its loading state instead of restoring the iframe.

I’m going to raise this internally, but could you share:

  • the App Bridge script/package version you’re using
  • browser + OS
  • whether this reproduces in a minimal app route outside your production app
  • a small repro repo or short screen recording if you have one (let me know if you’d rather set up a DM for this and I can get that set up)

Hope to hear from you soon!

Hey @Alan_G,

Thanks for getting back to me, and my sincere apologies for the direct tag! To give a little context, a teammate of mine posted about another App Bridge related issues over a month ago that unfortunately didn’t get any traction. Since this current bug is the absolute final blocker keeping us from launching our app, I tagged you out of a bit of desperation to make sure it didn’t get buried. I completely understand the rule, though, and will keep it in mind moving forward. Thank you for stepping in to help!

Here are the details to help raise this internally:

  • App Bridge version: Loaded via the global CDN script: [https://cdn.shopify.com/shopifycloud/app-bridge.js]

  • Browser + OS: Google Chrome running on Ubuntu.

  • Minimal app route: Yes, this reproduces completely outside of our production environment. The code snippet and screenshots I shared previously are from a barebones, isolated Nuxt 4 test app (bfs-nuxt4-app) used specifically to isolate this bug. After your message, I set up another app and made the bare minimum changes to make App Bridge work with a form, and I was able to replicate the issue there as well.

  • Repro/Recording: Here is a short screen recording showing the exact stalled state along with my project setup: https://www.awesomescreenshot.com/video/53253278?key=b62989c350a34d61b9d5e47d462777b1. I’d also be happy to share access to the minimal reproduction repo if the internal team needs to test the code directly.

Thanks again for escalating this. Let me know if you need anything else from my end.

Best,
Kamran

Thanks @Kamran_Khan - I’ll take a look into this further, I appreciate the extra info here!

Hey @Alan_G,

Hope you’re having a good week!

Just wanted to follow up on this since it’s been a little over a week. Has the internal team been able to review the screen recording and identify the issue?

Since this bug is still the final blocker holding up our app launch, any news on a potential fix, or even a temporary native workaround we could implement in the meantime, would be incredibly helpful.

Please let me know if the team needs access to the minimal reproduction repo or any additional testing from my end to help move this along.

Thanks again for your help!

Kamran

Hey @Kamran_Khan - thanks for the follow-up here.

I don’t have a fix ETA I can share yet, and I don’t see a supported native workaround for the browser refresh → Cancel path inside s-app-window at the moment. shopify.saveBar.leaveConfirmation() can help with app-controlled navigation, but this case is happening through the browser reload flow, where the App Window shell appears to stay in its loading state after the reload is cancelled.

The most launch-safe workaround for now would be to avoid placing that dirty editor flow inside s-app-window if possible, and use a regular embedded app route or another modal/page pattern until this is fixed, but I get that it’s not ideal.

I’ll keep you in the loop on this once I have next steps, really appreciate your patience.

Hey @Alan_G,

Thanks for the candid update and for looking into the workarounds.

To give you some context on why we chose <s-app-window>: we are building a drag-and-drop product feature that requires as much screen real estate as possible. Moving this to a standard embedded route creates significant spacing constraints on lower resolutions, and modals present similar layout limitations. The data-save-bar is crucial for this specific canvas to prevent users from accidentally losing their custom layouts.

We’ll figure out a temporary adjustment on our end so we can unblock our launch for now, but hopefully the internal team can get this resolved soon since the extra real estate is vital for this kind of feature.

Really appreciate you keeping me in the loop on this!

Best,
Kamran

Hey @Kamran_Khan - thanks for the context, just wanted to follow up here to let you know that we pushed a potential fix for this. A bit of good news! Can you let me know if you’re still seeing the issue on your side?

Also, a drag-and-drop editor sounds like a great use case for this extra workspace. Wishing you all the best with the launch, hope things go smoothly but please feel free to ping me here if I can help out further.

Hey @Alan_G,

That is amazing news, thank you so much to you and the team for turning this fix around so quickly!

I just tested the exact same dirty-input reload-cancel flow in both our isolated test app and our work-in-progress app, and I can confirm that the issue is completely resolved. When the reload is cancelled, the permanent loader no longer appears and the form stays in its exact state, just as it should.

We’re incredibly excited to finally push our app live now that this blocker is resolved. Thank you again for escalating this and for the launch well-wishes, it means a lot! You’re the best!

Best,
Kamran

No worries @Kamran_Khan - glad to hear everything is fixed! Best of luck with your app, let me know if we can help out with anything else :slight_smile: