Skip to content

feat: add configurable portal target - #500

Merged
farnabaz merged 2 commits into
vercel:mainfrom
slavco86:feat/mermaid-fullscreen-portal-container
Aug 26, 2026
Merged

feat: add configurable portal target#500
farnabaz merged 2 commits into
vercel:mainfrom
slavco86:feat/mermaid-fullscreen-portal-container

Conversation

@slavco86

@slavco86 slavco86 commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a top-level portal prop to configure the container used by Streamdown's built-in overlays. This keeps overlays inside a host subtree when using micro-frontends, scoped CSS, prefixed Tailwind utilities, or a custom stacking context.

Changes

  • Add portal?: HTMLElement | null | (() => HTMLElement | null) to Streamdown.
  • Use the configured target for:
    • Mermaid fullscreen
    • Table fullscreen
    • The built-in link safety modal
  • Keep document.body as the backward-compatible fallback.
  • Resolve getter targets only when an overlay opens, preserving SSR safety.
  • Recompute the memoized context when portal changes.
  • Document usage and add a patch changeset.

A custom linkSafety.renderModal remains host-controlled and therefore controls its own placement.

Testing

  • pnpm build:packages
  • pnpm build --filter website
  • pnpm test — 1,144 tests passed
  • pnpm check
  • Focused portal and overlay tests — 53 tests passed

Fixes #499

@vercel

vercel Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Someone is attempting to deploy a commit to the Vercel Team on Vercel.

A member of the Team first needs to authorize it.

Comment thread packages/streamdown/lib/mermaid/fullscreen-button.tsx Outdated
slavco86 pushed a commit to slavco86/streamdown that referenced this pull request Apr 9, 2026
Avoid accessing document during render when fullscreen is closed; addresses
review feedback on vercel#500.

@farnabaz farnabaz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @slavco86,
I like the idea of having portal container option, but we also have similar feature for link and table modals. It make no sense to have this feature for mermaid but not for others.

Instead of mermaid.fullscreenPortalContainer we can introduce portal directly as Streamdown prop and use it in all of modal.

Are you happy to update your PR to create this prop?

@slavco86

Copy link
Copy Markdown
Contributor Author

@farnabaz , thanks for picking this up and commenting!

Yes, absolutely - that makes perfect sense.

Leave it with me

@farnabaz

Copy link
Copy Markdown
Collaborator

@slavco86 one other thing, I appreciate if you sign all your commits, new and old ones. It is organization policy, all commits should be signed in order to merge a PR. (otherwise PR is blocked)

https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits

Allow built-in overlays to stay inside host CSS and stacking contexts by
sharing one top-level portal target across Mermaid fullscreen, table
fullscreen, and the link safety modal.

Refs vercel#499
@slavco86
slavco86 force-pushed the feat/mermaid-fullscreen-portal-container branch from 7f29b20 to 9bba54c Compare August 24, 2026 20:05
@slavco86 slavco86 changed the title feat: configurable portal target for Mermaid fullscreen feat: add configurable portal target Aug 24, 2026
@slavco86

Copy link
Copy Markdown
Contributor Author

@farnabaz Updated as requested:

  • Replaced the Mermaid-specific option with a top-level portal prop.
  • Applied it to Mermaid fullscreen, table fullscreen, and the built-in link safety modal (all current createPortal call sites).
  • Rebased the PR onto current main; it is now conflict-free.
  • Replaced the old unsigned history with one GitHub-verified signed commit.
  • Added integration tests, SSR-lazy-resolution coverage, docs, and a patch changeset.

The full package build, website build, 1,144-test suite, and lint/format checks pass locally. Ready for another review when convenient.

@slavco86
slavco86 requested a review from farnabaz August 24, 2026 20:07
Comment thread packages/streamdown/index.tsx Outdated
prevProps.plugins === nextProps.plugins &&
prevProps.className === nextProps.className &&
prevProps.linkSafety === nextProps.linkSafety &&
prevProps.portal === nextProps.portal &&

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@slavco86 If we pass function to portal, when other props change, component will re-render because portal will receive new instance.
I think it is good to remove this and treat portal as initializing props like other function props (urlTransform and allowElement)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@farnabaz , good point! Thanks. Updated

Remove `portal` from the memo comparator. `PortalTarget` allows a
`() => HTMLElement | null` getter, so an inline getter produced a new
function identity on every parent render and defeated memoization for
every consumer using that form.

`portal` now behaves like the other function props (`urlTransform`,
`allowElement`), which are likewise absent from the comparator. The
getter form remains the recommended way to target an element that is
assigned late, since it is resolved each time an overlay opens rather
than at render time.
@slavco86
slavco86 requested a review from farnabaz August 26, 2026 12:15

@farnabaz farnabaz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

@farnabaz
farnabaz merged commit 18dcb20 into vercel:main Aug 26, 2026
5 of 6 checks passed
lofcz added a commit to lofcz/streamdown-ng that referenced this pull request Aug 28, 2026
Keep the fork's package identity, deferred streaming blocks, and streamdown-context. Take unique upstream bits: configurable overlay portal (vercel#500), geistdocs 1.23.1, and the seroval bump. Skip upstream's Vercel deploy step and 2.6.0 version bump — the fork is already at 2.11.0 / remend 1.4.2.
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.

feat(mermaid): configurable portal target for Mermaid fullscreen (micro-frontends & prefixed CSS)

2 participants