@shopify/app-bridge-react bug with multiple modals

Topic summary

Bug: In embedded apps using @shopify/app-bridge-react, multiple Modal components conflict—whichever Modal is rendered last in the component tree is displayed, regardless of which one has open=true.

Reproduction and behavior:

  • A demo with Polaris AppProvider and App Bridge Provider shows that setting showModal1=true still displays “Modal 2” because it’s rendered later.
  • This occurs even with deeply nested components.
  • Conditional rendering of a single Modal at a time opens the correct one, but programmatically closing via the open prop isn’t feasible without orchestrating multiple renders.

Context:

Latest updates:

  • Shopify acknowledged the report and escalated it internally for a team response.
  • Another developer confirmed encountering the same issue and requested a solution.

Status and outcomes:

  • No fix or workaround officially provided yet; discussion remains open.
  • Temporary workaround: conditionally render only the active Modal, though it limits programmatic close control.
Summarized with AI on December 11. AI used: gpt-5.

Originally reported against the @shopify/polaris project on GitHub: https://github.com/Shopify/polaris-react/issues/2009

When using the Modal component from the app bridge react components in an embedded app, if you have multiple modals rendering it will always display the last modal in the component hierarchy even when specifying the open prop for any others.

No matter what Modal has the open prop set to true, the last modal rendered in the entire component hierarchy is the one that is displayed. This is true for components that are deeply nested too. You may change a prop on a Modal in a component based on local state and some other Modal in a disparate part of the component hierarchy will display if it’s rendered later.

The following is a reduced test case that can be put into a create-react-app project with Polaris and app-bridge-react dependencies.

import React, { Component } from 'react';
import {
  AppProvider,
  Page,
  Layout,
  Button
} from '@shopify/polaris';
import {
  Provider,
  Modal
} from '@shopify/app-bridge-react';

const API_KEY = 'CHANGE_ME_1';
const SHOP_ORIGIN = 'CHANGE_ME_2';

export default class App extends Component {
  state = {
    showModal1: false,
    showModal2: false
  }
  
  render() {
    const { showModal1, showModal2 } = this.state;
    const config = {
      apiKey: API_KEY,
      shopOrigin: SHOP_ORIGIN,
      forceRedirect: true 
    };
    return (
      <AppProvider>
        <Provider config={config}>
          <Page>
            <Layout sectioned>
              <Button onClick={() => this.setState({ showModal1: true })} fullWidth>Display modal</Button>
            </Layout>
            <Modal
              title="Modal 1"
              message="Modal 1"
              onClose={() => this.setState({ showModal1: false })}
              open={showModal1} />
            <Modal
              title="Modal 2"
              message="Modal 2"
              onClose={() => this.setState({ showModal2: false })}
              open={showModal2} />
          </Page>
        </Provider>
      </AppProvider>
    );
  }
}

Clicking the Button sets the showModal1 state to true and should display “Modal 1” but “Modal 2” shows up instead. Conditionally rendering the modals does open the correct Modal, but closing them programmatically with the open prop isn’t really possible unless you do it across two separate renders.

Hey @MarcBaumbach ,

You reported this correctly, I reached out to someone on our team to have them respond so you can discuss the issue with them there. Best of luck!

I’m also facing the same issue. How to solve it?