Composition

SuggestMenu can be used in two ways: a simple API (props on <SuggestMenu>) or a composable API (useSuggestMenu + menu prop). Layout can be floating (Portal + Positioner + Popup) or inline (list as direct children).

Simple vs composable API

Simple: Pass editor, triggerCharacter, items, and other options directly to <SuggestMenu>. The component manages state internally. Best when the menu is the only thing that needs to react to the session.

Composable: Call useSuggestMenu(editor, options) and pass the returned instance directly to <SuggestMenu menu={instance}>. Use this when you need menu state outside the tree (e.g. parent wrappers, animations, conditional layout).

Floating vs inline layout

Floating: Use SuggestMenu.Portal + Positioner + Popup so the list is portaled and positioned near the cursor. This is the typical “dropdown” pattern.

Inline: Omit Portal/Positioner/Popup and render SuggestMenu.List (and children) as direct children of SuggestMenu. The list appears in the DOM where you put it (e.g. below the input in a chat UI). Same API for items and keyboard; only the layout differs.

Patterns

  • Standard floating popup — one SuggestMenu with Portal → Positioner → Popup → List.
  • Chat input with inline expanding panel (Granola-style) — no Portal; list in a panel that expands below the input.
  • Notion-style editor — multiple SuggestMenu roots (e.g. @, /, #) in one editor; only one session active at a time.
  • Custom panel driven by useSuggestMenu — use the hook, render your own wrapper and list with the instance.