Skip to content

feat(Toast): containerRef and global show option#621

Open
malinindev wants to merge 2 commits into
reshaped-ui:canaryfrom
malinindev:toast-container-ref
Open

feat(Toast): containerRef and global show option#621
malinindev wants to merge 2 commits into
reshaped-ui:canaryfrom
malinindev:toast-container-ref

Conversation

@malinindev

@malinindev malinindev commented May 27, 2026

Copy link
Copy Markdown

Summary

Two related additions to <ToastProvider> / useToast, both about decoupling where toasts are rendered from where the provider sits in the React tree:

1. containerRef prop on <ToastProvider>

When provided, toast regions are rendered into the referenced DOM element via ReactDOM.createPortal instead of inline next to children.

  • Context scope (which useToast() consumers see the provider) — still follows the React tree, unchanged.
  • DOM position of the toast regions — can now be anchored to an arbitrary CSS-positioned element.

Mirrors the existing containerRef pattern already used by <Flyout>.

2. global option in show

toast.show({ ..., global: true }) renders the toast in the root provider, ignoring any nested provider's containerRef. Useful when your whole app is wrapped in a containerRef provider but you occasionally need a toast on top of everything (e.g. a critical error), without restructuring the tree. toast.hide(id, { global: true }) mirrors it.

Screenshots / Recordings

image

Notes for Reviewers

  • containerRef is additive (~10 lines in ToastProvider.tsx); no changes to ToastRegion, useToast, or Toast.module.css. The target element must be CSS-positioned (documented in the prop's JSDoc), since the nested region relies on position: absolute.
  • global works by each provider tracking the topmost provider in the chain via context; show/hide delegate there when the flag is set. Only add/hide are global-aware (the public API) — show/remove are internal lifecycle calls handled by the root region itself.
  • Two Storybook stories (containerRef, global) cover both the visual demo and the play() assertions.
  • Changeset added (minor bump).

@malinindev malinindev force-pushed the toast-container-ref branch 2 times, most recently from 0c47393 to f9775b6 Compare May 27, 2026 02:57
@malinindev

Copy link
Copy Markdown
Author

@blvdmitry I'd be happy to see this small feature in the next release.
Also I faced a CI issue with Dependencies setup, it's caused by a pnpm version conflict. Created a separate PR: #622

@malinindev malinindev changed the title feat(Toast): containerRef feat(Toast): containerRef and global show option May 27, 2026
@blvdmitry

blvdmitry commented May 27, 2026

Copy link
Copy Markdown
Contributor

@malinindev Global is a good improvement. For the container ref - does it cover any additional use cases that nested ToastProviders don't cover today? There is a story with a demo of those

@malinindev

Copy link
Copy Markdown
Author

@malinindev Global is a good improvement. For the container ref - does it cover any additional use cases that nested ToastProviders don't cover today? There is a story with a demo of those

Actually, containerRef is the crucial feature for me in the PR, global is just an addition :)
In our application we have a layout with:

  1. Header with tools
  2. Left sidebar
  3. Main Area (for render pdf document)
  4. Right sidebar

Toasts should always be shown in the Main Area only (we render pdf document there).

The other parts have their own specific features, related to the document in Main Area but aren't called inside Main Area. And we cannot move all the functionality into Main Area, because it's used for render a document only.

So we have such a layout (roughly):

<div>
 <ToastProvider>
  <Header>
  <div>
    <Sidebar1 />
    <MainArea />
    <Sidebar2 />
  </div>
 <ToastProvider />
</div>
image

If we call toast from the left sidebar like this

  toast.show({
    position: 'top-start',
  })

I see:
image

And what I'm supposed to see:
image

And there is no way to do correctly it with the current toast API (I only achieved it in our App with extremely ugly code)

But with the new API with containerRef, provided in this PR, we can do just:

<div>
 <ToastProvider containerRef={mainAreaRef}>
  <Header>
  <div>
    <Sidebar1 />
    <MainArea ref={mainAreaRef}/>
    <Sidebar2 />
  </div>
 <ToastProvider />
</div>

And all problems will be solved

@malinindev

Copy link
Copy Markdown
Author

Also, I was hesitating about global api.

I chose

toast.show({
  global: true,
  title: 'halo',
})

But another solution could be

toast.showGlobal({
  title: 'halo',
})

Which is preferable here - up to you @blvdmitry

@blvdmitry

Copy link
Copy Markdown
Contributor

@malinindev so it feels like this example is similar to what you're trying to achieve if you wrap main area with ToastProvider? https://www.reshaped.so/docs/components/toast#render-boundaries

@malinindev

malinindev commented May 31, 2026

Copy link
Copy Markdown
Author

if you wrap main area with ToastProvider?

Not really. If I wrap MainArea in a ToastProvider, the toast will be shown within its boundaries only if I call toast.show() inside MainArea.

I need to call toast.show() inside Sidebar, but the toast should be shown inside MainArea. You cannot achieve it without containerRef only by wrapping.

I cannot movetoast.show() call inside the MainArea component for several reasons. In our app, MainArea is just a container for displaying a rendered PDF document. To simplify, you can think of it as a canvas.

Here is an example in sandbox: https://codesandbox.io/p/sandbox/bitter-bash-yxf29l

If this can be achieved with the standard ToastProvider, could you please show how we can call toast.show() inside the Sidebar component while still rendering the toast within the boundaries of MainArea?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants