Update: It seems that there's no simple way to do this without rendering the whole app inside an iframe
.
If you don't mind doing that though, it's easy enough (taken from How to set iframe content of a react component):
const Frame = ({ children }) => {
const [ref, setRef] = useState(null)
const mountNode = ref?.contentWindow.document.body
return (
<iframe title="iframe" ref={setRef}>
{mountNode && ReactDOM.createPortal(children, mountNode)}
</iframe>
)
}
Then, wrap your main app as a child of the Frame
component. Clicks inside an iframe
will not bubble up to the parent window, as detailed in Capture click on div surrounding an iframe.
Here's a CodeSandbox demo showing this approach.
Original (non-functioning) answer below
On your App
or Router
component:
const blockClicks = useCallback((e) => {
const { target } = e
const { nodeName, href } = target
if (
target.closest('#root-id') &&
nodeName === 'A' &&
href &&
(/^https?:\/\//.test(!href) ||
href.startsWith(window.location.origin)
)) {
// amend logic specific to your use case
// - we want to avoid blocking irrelevant clicks
e.stopPropagation()
}
})
useEffect(() => {
document.addEventListener('click', blockClicks, true)
return () =>
document.removeEventListener('click', blockClicks, true)
})