API for overriding browser's context such as the reference `window`
Description
Currently, Puck internally references the global window object and other browser APIs directly, which causes issues when the editor is embedded within complex environments like Shopify embedded apps, iframes, or React portals within iframes. In these scenarios, the drag and drop functionality breaks because Puck is referencing the wrong window context - typically the parent/main application's window instead of the modal or iframe's window where the actual interaction is taking place.
This limitation prevents Puck from working correctly in:
- Shopify embedded app modals
- Applications using React portals for modals
- Multi-frame applications
- Any scenario where the editor needs to reference a different browser context than the main window
The expected outcome is to have an API that allows developers to override the browser context that Puck uses internally, ensuring drag and drop and other browser-dependent features work correctly regardless of the embedding environment.
Considerations
- This feature would need to be backward compatible and not break existing implementations
- Performance should not be significantly impacted by adding this abstraction layer
- TypeScript support should be maintained with proper typing for the browser context overrides
- The solution should work with both direct usage and when Puck is rendered inside iframes or portals
Proposals
Proposal 1: Browser Context Override in Overrides API
Extend the existing overrides prop to include a browserContext property that allows developers to override browser globals:
<Puck
config={puckConfig}
data={puckData}
overrides={{
header: CustomHeader,
iframe: CustomIframe,
browserContext: {
window: modalWindow, // Reference to the modal's window
// Other browser APIs as needed
}
}}
/>
Implementation approach:
- Create a
BrowserContextReact context that holds references to browser APIs - Provide a
BrowserContextProviderthat wraps Puck's internal components - Create a custom hook
useBrowserContext()that components can use to access browser APIs - Replace all direct browser API usage within Puck with calls to
useBrowserContext()
Example internal usage:
// Instead of: window.addEventListener(...)
const { window: contextWindow } = useBrowserContext();
contextWindow.addEventListener(...);
Pros:
- Seamlessly integrates with existing
overridesAPI - Maintains backward compatibility (defaults to global browser objects)
- Flexible - allows partial overrides
- Clear and intuitive API for developers
Cons:
- Requires internal refactoring of Puck's codebase
This feature would significantly improve Puck's usability in embedded applications and complex UI architectures, making it a more robust solution for enterprise applications that often require such flexibility.
This appears to be blocked by https://github.com/Shopify/shopify-app-bridge/issues/518
Hi @xaviemirmon !
I was able to get the window of any element that is transported to a different iframe after rendering!
It's a bit hacky but I think it can help us run an effect to reference the correct window!
Here:
const windowOfElement = element.ownerDocument.defaultView;
This basically gives the owning window of an element at a given time. I tried it and once puck is translated to Shopify portal's iframe for the Modal it correctly shows me the modal window!
with that window only thing left is puck allowing us to re-render with the new window reference.