Selection

A floating toolbar that follows a text selection, anchored to the start of the highlight.

Anatomy

A scoped root that listens for selection events inside a child region. When a non-empty selection settles after pointerup, the toolbar appears in a portal at the start of the first visual rect. The action buttons receive the selected text and decide what happens next.

TSX

The component is unopinionated about actions. Add any combination of buttons — copy, quote, share, highlight, define, cite. Each button receives the selected text and decides what to do with it.

Behavior

  • The toolbar appears once a selection settles after pointerup or touchend, never during the drag. Following the cursor mid-selection feels wrong, so the toolbar waits for the user to release.
  • Selections that start or end outside SelectionContent are ignored. Two Selection roots on the same page do not interfere with each other.
  • When the preferred side will not fit, the toolbar flips to the other side. When it still does not fit either way, it clamps within edgePadding of the viewport edge. The actual rendered side lands on data-side for consumers who want to style it.
  • The toolbar repositions on scroll and resize, including the visual viewport, so the on-screen keyboard and pinch zoom keep things anchored.
  • Clicking the toolbar does not deselect the text. The toolbar disables its own mousedown so the highlight survives the click.

Dismiss

The toolbar closes on any of these events. All of them fire onOpenChange(false) if the consumer is listening.

  • The selection collapses, either because the user clicks elsewhere or starts a new selection.
  • The user clicks outside both SelectionContent and the toolbar.
  • The user presses Escape.
  • A SelectionButton action runs with clearOnSelect or closeOnSelect (both true by default).

Mobile

The native callout from iOS and Android shares the screen with the toolbar. The component does not try to suppress the OS menu — it sits below it and relies on the user dismissing whichever one they do not want. The toolbar repositions against window.visualViewport, so the on-screen keyboard does not push it off-screen.

Selection still works through touch: long-press to start, drag the handles to adjust, lift to commit. The same onSelect and SelectionButton handlers fire that fire on desktop.

API

Reference for each part of the component, including its available props and behavior.

Selection

The root. Owns selection state, attaches the document-level listeners scoped to SelectionContent, and renders nothing of its own. Pass it children including a SelectionContent and a SelectionToolbar.

The root has no DOM. It only provides context. The actual selectable region is whatever SelectionContent wraps, and the toolbar is rendered through a portal to document.body so layout containers do not clip it.

Prop

SelectionContent

The scope. Anchors the listeners to a specific subtree. Selections that touch any node outside this subtree are ignored, so the toolbar only opens for text the consumer marked as eligible.

The element itself is unstyled. Add classes for whatever typography the content needs.

Prop

SelectionToolbar

The floating surface. Portals to document.body, fixes itself to the viewport, and anchors to the first visual rect of the current selection. Repositions on scroll and resize, flips to the opposite side when the preferred side does not fit, and clamps within the viewport's edgePadding.

Toolbar size is taken from the rendered content, so widths can vary by how many actions the consumer drops in.

Prop

SelectionButton

An action button bound to the toolbar. Extends the kit's Button with a ghost default variant and tighter spacing, and adds onSelect to receive the selected text.

TSX

Two flags control the post-click behavior. clearOnSelect clears the browser selection (the highlight disappears, the toolbar closes via the selectionchange listener). closeOnSelect only hides the toolbar without touching the highlight, useful for highlight-then-annotate flows. Both default to true.

TSX

Call event.preventDefault() in onClick to block both the action and the close. The onClick runs before onSelect, so this is the place to gate the action behind a confirmation.

Prop

SelectionSeparator

A thin vertical divider for visual grouping inside the toolbar. Inert and aria-hidden.

Prop

useSelection

Hook for reading the current selection from a custom part inside the root. Use it when building a custom action that needs the selection but does not want the styling of SelectionButton, or to drive an outside affordance from the same state.

TSX

Returns:

  • text — the selected text as a string. Empty string when nothing is selected.
  • range — the live Range object backing the selection, or null when nothing is selected. Live, so it tracks edits the user makes to the same selection.
  • rect — the first visual DOMRect of the range (the start of the selection in viewport coordinates), or null when nothing is selected. Snapshot, so consumers anchoring their own UI to the selection should recompute from range on scroll.
  • open — whether the toolbar is currently visible. Reflects the controlled or uncontrolled state of the root.
  • dismiss() — closes the toolbar without clearing the highlight.
  • clearSelection() — clears the browser selection, which in turn closes the toolbar through the selectionchange listener.

dismiss and clearSelection are exposed separately so consumers can compose either behavior. Most actions want both, which is the default for SelectionButton.