Skip to content

fix: Suggestion menu overflow issue - #2363

Closed
matthewlipski wants to merge 5 commits into
mainfrom
suggestion-menu-overflow-fix
Closed

matthewlipski wants to merge 5 commits into
mainfrom
suggestion-menu-overflow-fix

Conversation

@matthewlipski

@matthewlipski matthewlipski commented Jan 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes an issue when on initial show, the suggestion menu would not correctly flip vertically if there was no space, and would only flip once the items updated.

This would happen because the GenericPopover responsible for positioning and sizing the suggestion menu would do so based on its initial size. However, the menu fetches and renders its items async, so its initial size is far smaller than when it has all of its items.

To fix this, the GenericPopover's position is re-calculated when the suggestion menu items are updated.

Rationale

The suggestion menu issue is pretty annoying for users and causes bad UX.

Changes

  • Added GenericPopoverUpdateContext which contains a function to update the position of the nearest GenericPopover.
  • Made SuggestionMenuWrapper update ancestor GenericPopover position when after items are updated.
  • Changed autoPlacement middleware to flip (functions same as before, but autoPlacement was inconsistent after this change).
  • Added max height of 600px to SuggestionMenuController.
  • Applied same changes to grid suggestion menu.

Impact

N/A

Testing

N/A

Screenshots/Video

N/A

Checklist

  • Code follows the project's coding standards.
  • Unit tests covering the new feature have been added.
  • All existing tests pass.
  • The documentation has been updated to reflect the new feature

Additional Notes

@vercel

vercel Bot commented Jan 15, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
blocknote Ready Ready Preview Jan 16, 2026 5:42pm
blocknote-website Ready Ready Preview Jan 16, 2026 5:42pm

Request Review

Comment thread packages/react/src/components/Popovers/FloatingUIReturns.tsx Outdated
Comment thread packages/react/src/components/Popovers/GenericPopover.tsx Outdated
@pkg-pr-new

pkg-pr-new Bot commented Jan 15, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/ariakit@2363

@blocknote/code-block

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/code-block@2363

@blocknote/core

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/core@2363

@blocknote/mantine

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/mantine@2363

@blocknote/react

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/react@2363

@blocknote/server-util

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/server-util@2363

@blocknote/shadcn

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/shadcn@2363

@blocknote/xl-ai

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-ai@2363

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-docx-exporter@2363

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-email-exporter@2363

@blocknote/xl-multi-column

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-multi-column@2363

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-odt-exporter@2363

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-pdf-exporter@2363

commit: c910dfd

@nperez0111

Copy link
Copy Markdown
Contributor

Fix looks like it works to me!

@YousefED

Copy link
Copy Markdown
Collaborator

Haven't looked at code yet, just the demo. I noticed the menu can still get very tall:

image

Could we look into limiting this and do an overall check on the UX (compare with other apps)? Up to you if it should be part if this PR or separate

@matthewlipski

Copy link
Copy Markdown
Collaborator Author

I added a max height of 600px - I think for this PR though, an overall UX fix-up is out of scope

@nperez0111 nperez0111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'll let @YousefED take one last look at this, since he knows best the edge cases. But this feels right to me

@YousefED

Copy link
Copy Markdown
Collaborator

Great! The UX is a lot better. One minor bug is the page scroll shifts when navigating with keyboard (hold an arrow key to cycle through the list completely). Not sure if that's an easy fix?

Regarding the new Context; shouldn't autoUpdate detect content size changes automatically? https://floating-ui.com/docs/autoupdate#elementresize

@matthewlipski

Copy link
Copy Markdown
Collaborator Author

The page scroll shift bug looks like it's unrelated to the changes in this PR (also reproducible on website & main), so I think it's out of scope. I'll make a new issue for it though.

As for the autoUpdate question, that's a good point. I took some time to look into that, and it does seem like autoUpdate is getting triggered from the resize as expected. However, that doesn't seem to re-trigger the middleware? This is the part I'm confused about, since calling update manually works just fine.

@YousefED

Copy link
Copy Markdown
Collaborator

The page scroll shift bug looks like it's unrelated to the changes in this PR (also reproducible on website & main), so I think it's out of scope. I'll make a new issue for it though.

Makes sense!

As for the autoUpdate question, that's a good point. I took some time to look into that, and it does seem like autoUpdate is getting triggered from the resize as expected. However, that doesn't seem to re-trigger the middleware? This is the part I'm confused about, since calling update manually works just fine.

I had a quick look, I think it does get triggered, but maybe we need to configure it differently? I found this guide, but didn't immediately found a fix.

A different direction I'm thinking of would be to always set a fixed height of 600(?) and measure based of that (or use a virtual element to return 600). That would also solve a requirement that imo we don't want the menu to flip once the users filters and less options get available -> menu resizes -> menu flips (imo it's better to have a stable side).

Can you experiment a bit further based of this? I agree there are quite some edge cases involved!

@nperez0111

nperez0111 commented Jan 27, 2026 •

Copy link
Copy Markdown
Contributor

I think autoUpdate depends on there being an actual element, not just a virtual element, to attach the resize observer onto. It's unclear to me whether we currently are giving it a real element or not.

I think that the real problem here is that the suggestion menu items are being rendered asynchronously no? Like why are we doing all of these workarounds for something that is actually synchronous most of the time?

Even if not that, I think that autoUpdate is just an abstraction over doing the same sort of thing that we are doing here anyway no? It will just call update to re-calc and re-layout, so I don't understand why we are putting any more effort here if this solution is sufficient?

@YousefED

Copy link
Copy Markdown
Collaborator

I think autoUpdate depends on there being an actual element, not just a virtual element, to attach the resize observer onto. It's unclear to me whether we currently are giving it a real element or not.

We're passing a real element, so autoupdate should work

I think that the real problem here is that the suggestion menu items are being rendered asynchronously no? Like why are we doing all of these workarounds for something that is actually synchronous most of the time?

The Suggestion Menus are designed to be Async compatible, because users should be able to reuse the menu for things like user mentions (which might require an API call)

Even if not that, I think that autoUpdate is just an abstraction over doing the same sort of thing that we are doing here anyway no? It will just call update to re-calc and re-layout, so I don't understand why we are putting any more effort here if this solution is sufficient?

Two reasons top of mind:

  • Keep a clean codebase. We invested a lot in the UI Components refactor to clean up this part of the codebase. I want to be careful to introduce workarounds too easily if the functionality can be provided by Floating UI
  • By understanding the problem and Floating UI better, we might be able to find a clean solution that for example also fixes the "flipping while filtering" UX I described above (instead of introducing another work around for that one as well)

YousefED added a commit that referenced this pull request Oct 13, 2026
… at 600px

The side now depends only on the trigger and the visible area (a fixed
200px menu), so nothing needs to be remembered: filtering cannot flip
the menu, and reopening, scrolling or resizing re-choose the side. Uses
platform.detectOverflow like autoPlacement does, and measures above
only when the menu does not fit below.

Raises the maximum height to 600px. Adds tests for asynchronously
loaded items (#2363), page scroll, window resize, and the gap to the
trigger while filtering (#2155).
@YousefED

Copy link
Copy Markdown
Collaborator

#3190 (draft) addresses the same problem with the approach discussed above: the side is decided from a fixed height, not the measured menu, so it does not depend on when the items load, and the menu keeps its side while filtering. It needs no context or update() call, and has a 600px maximum height as well. If #3190 is accepted, this PR can be closed.

YousefED added a commit that referenced this pull request Oct 13, 2026
…side and width while filtering (#3190)

* fix(core): don't reposition the suggestion menu on its own scroll

The scroll listener captures every scroll in the editor, including the
menu's own. Each one recomputed the menu's position and re-rendered it,
so a menu with little room could not be scrolled to its end. Only a
scroll of an element that contains the trigger can move it.

Part of #2771

* fix(react): place the suggestion menu below unless cramped, keep its side while filtering

The size middleware capped the menu to the room on its side, and
autoPlacement then measured the capped menu: it fit where it was, so a
menu near the bottom of the screen stayed squeezed below the trigger,
and in WebKit it swung between the sides.

A placement middleware now keeps the menu below unless there is less
than 200px of room there and more above. It chooses the side when the
menu opens and again only when the trigger moves, so the menu does not
jump while the user filters the items. Both suggestion menu controllers
use it.

Closes #2771

* fix(react): cap the suggestion menu at 400px

With a lot of room, the menu took nearly the whole height of the screen.
It now takes at most 400px; a longer list scrolls.

* fix(react): decide the suggestion menu's side from geometry only, cap at 600px

The side now depends only on the trigger and the visible area (a fixed
200px menu), so nothing needs to be remembered: filtering cannot flip
the menu, and reopening, scrolling or resizing re-choose the side. Uses
platform.detectOverflow like autoPlacement does, and measures above
only when the menu does not fit below.

Raises the maximum height to 600px. Adds tests for asynchronously
loaded items (#2363), page scroll, window resize, and the gap to the
trigger while filtering (#2155).

* test: update slash menu screenshots for the 600px cap; place the cursor through the API

The dark slash menu and the slash menu page up/down screenshots show the
menu capped at 600px. They are also about 11px narrower than the old
images: that difference predates this change (the old images were
captured in an environment that renders the menu wider; main renders it
339px wide too) and stayed within the screenshot tolerance until now.

The placement tests place the cursor through the editor API: a window
resized with page.viewport is scaled, so mouse coordinates miss. The
async items test now asserts that the menu is still empty at its first
check.

* fix(react): keep the suggestion menu's width while filtering

The menu was as wide as its widest item, so it shrank and grew while the
user filtered the items. The middleware now keeps the floating element at
the widest width the menu has had since it opened, and the menu fills the
floating element (one shared CSS rule). The menu can still grow, e.g.
when items load asynchronously; each opening starts at its own width.

* test: reopening the suggestion menu, and wait for WebKit's late scroll event

Adds tests that a menu opened again elsewhere chooses its side and height
again, and starts at its own width. The page scroll tests wait for the
expected state instead of a fixed second: WebKit delivers the scroll
event about a second late.
@YousefED

Copy link
Copy Markdown
Collaborator

Closing in favour of #3190 (merged), which fixes the same problem along the lines discussed above: the side is decided from a fixed height, so it does not depend on when the items load, and the menu keeps its side and width while filtering. It also has the 600px maximum height. Thanks for the groundwork here!

@YousefED YousefED closed this Oct 13, 2026

This branch was successfully deployed

2 active deployments
Preview – blocknote-website — c910dfdc Deployed Jan 16, 2026 by vercel[bot]
Preview – blocknote — c910dfdc Deployed Jan 16, 2026 by vercel[bot]
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.

3 participants