Tooltip

Display container for Tooltip content. Has a directional arrow dependent on its placement.

installyarn add @adobe/react-spectrum
added3.4.0
usageimport {Tooltip, TooltipTrigger} from '@adobe/react-spectrum'

Examples#


<TooltipTrigger>
  <ActionButton aria-label="Edit Name"><Edit /></ActionButton>
  <Tooltip>Change Name</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
  <ActionButton aria-label="Edit Name">
    <Edit />
  </ActionButton>
  <Tooltip>Change Name</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
  <ActionButton aria-label="Edit Name">
    <Edit />
  </ActionButton>
  <Tooltip>
    Change Name
  </Tooltip>
</TooltipTrigger>

Content#


The TooltipTrigger accepts exactly two children: the element which triggers the display of the Tooltip and the Tooltip itself. The trigger element must be the first child passed into the TooltipTrigger. All content should be internationalized.

Accessibility#

Tooltip triggers must be focusable and hoverable in order to ensure that all users can activate them. When displayed, TooltipTrigger automatically associates the tooltip with the trigger element so that it is described by the tooltip content.

Tooltip Delay#


Tooltips should appear after a short delay when hovering the trigger, or instantly when using keyboard focus. This delay can be adjusted for hover. View guidelines

<TooltipTrigger delay={0}>
  <ActionButton aria-label="Save"><Save /></ActionButton>
  <Tooltip>Saving applies your new settings right away.</Tooltip>
</TooltipTrigger>
<TooltipTrigger delay={0}>
  <ActionButton aria-label="Save">
    <Save />
  </ActionButton>
  <Tooltip>
    Saving applies your new settings right away.
  </Tooltip>
</TooltipTrigger>
<TooltipTrigger
  delay={0}
>
  <ActionButton aria-label="Save">
    <Save />
  </ActionButton>
  <Tooltip>
    Saving applies your
    new settings right
    away.
  </Tooltip>
</TooltipTrigger>

Warmup / Cooldown#

Tooltips have a warm up and cool down period, see guidelines. Only one tooltip can be open at a time.

<Flex gap="size-200">
  <TooltipTrigger>
    <ActionButton>Hover me</ActionButton>
    <Tooltip>I come up after a delay.</Tooltip>
  </TooltipTrigger>
  <TooltipTrigger>
    <ActionButton>Then hover me</ActionButton>
    <Tooltip>If you did it quickly, I appear immediately.</Tooltip>
  </TooltipTrigger>
</Flex>
<Flex gap="size-200">
  <TooltipTrigger>
    <ActionButton>Hover me</ActionButton>
    <Tooltip>I come up after a delay.</Tooltip>
  </TooltipTrigger>
  <TooltipTrigger>
    <ActionButton>Then hover me</ActionButton>
    <Tooltip>
      If you did it quickly, I appear immediately.
    </Tooltip>
  </TooltipTrigger>
</Flex>
<Flex gap="size-200">
  <TooltipTrigger>
    <ActionButton>
      Hover me
    </ActionButton>
    <Tooltip>
      I come up after a
      delay.
    </Tooltip>
  </TooltipTrigger>
  <TooltipTrigger>
    <ActionButton>
      Then hover me
    </ActionButton>
    <Tooltip>
      If you did it
      quickly, I appear
      immediately.
    </Tooltip>
  </TooltipTrigger>
</Flex>

Tooltip placement#


Tooltips support a variety of placement options.

Placement#

The Tooltip's placement with respect to its trigger element can be adjusted using the placement prop. See the props table below for a full list of available placement combinations.

<TooltipTrigger placement="end">
  <ActionButton aria-label="Foo">Placement</ActionButton>
  <Tooltip>
    In left-to-right, this is on the right. In right-to-left, this is on the
    left.
  </Tooltip>
</TooltipTrigger>
<TooltipTrigger placement="end">
  <ActionButton aria-label="Foo">Placement</ActionButton>
  <Tooltip>
    In left-to-right, this is on the right. In
    right-to-left, this is on the left.
  </Tooltip>
</TooltipTrigger>
<TooltipTrigger placement="end">
  <ActionButton aria-label="Foo">
    Placement
  </ActionButton>
  <Tooltip>
    In left-to-right,
    this is on the
    right. In
    right-to-left, this
    is on the left.
  </Tooltip>
</TooltipTrigger>

Offset and cross offset#

The Tooltip's offset with respect to its trigger can be adjusted using the offset and crossOffset props. The offset prop controls the spacing applied along the main axis between the element and its trigger whereas the crossOffset prop handles the spacing applied along the cross axis.

Below is a tooltip offset by an additional 50px above the trigger.

<TooltipTrigger offset={50}>
  <ActionButton aria-label="Offset from trigger">Offset</ActionButton>
  <Tooltip>This will shift up.</Tooltip>
</TooltipTrigger>
<TooltipTrigger offset={50}>
  <ActionButton aria-label="Offset from trigger">
    Offset
  </ActionButton>
  <Tooltip>This will shift up.</Tooltip>
</TooltipTrigger>
<TooltipTrigger
  offset={50}
>
  <ActionButton aria-label="Offset from trigger">
    Offset
  </ActionButton>
  <Tooltip>
    This will shift up.
  </Tooltip>
</TooltipTrigger>

Below is a tooltip cross offset by an additional 100px to the right of the trigger.

<TooltipTrigger crossOffset={100} placement="bottom">
  <ActionButton aria-label="Cross Offset from trigger">
    Cross Offset
  </ActionButton>
  <Tooltip>This will shift over to the right.</Tooltip>
</TooltipTrigger>
<TooltipTrigger crossOffset={100} placement="bottom">
  <ActionButton aria-label="Cross Offset from trigger">
    Cross Offset
  </ActionButton>
  <Tooltip>This will shift over to the right.</Tooltip>
</TooltipTrigger>
<TooltipTrigger
  crossOffset={100}
  placement="bottom"
>
  <ActionButton aria-label="Cross Offset from trigger">
    Cross Offset
  </ActionButton>
  <Tooltip>
    This will shift
    over to the right.
  </Tooltip>
</TooltipTrigger>

Events#


TooltipTrigger accepts an onOpenChange handler which is triggered whenever the Tooltip is shown or hidden.

The example below uses onOpenChange to update a separate element with the current open state of the Dialog.

function Example() {
  let [isOpen, setOpen] = React.useState(false);

  return (
    <Flex alignItems="center" gap="size-100">
      <TooltipTrigger isOpen={isOpen} onOpenChange={setOpen}>
        <ActionButton aria-label="Resize"><Resize /></ActionButton>
        <Tooltip>Resize text.</Tooltip>
      </TooltipTrigger>
      <Text>Tooltip is {isOpen ? 'showing' : 'not showing'}</Text>
    </Flex>
  );
}
function Example() {
  let [isOpen, setOpen] = React.useState(false);

  return (
    <Flex alignItems="center" gap="size-100">
      <TooltipTrigger
        isOpen={isOpen}
        onOpenChange={setOpen}
      >
        <ActionButton aria-label="Resize">
          <Resize />
        </ActionButton>
        <Tooltip>Resize text.</Tooltip>
      </TooltipTrigger>
      <Text>
        Tooltip is {isOpen ? 'showing' : 'not showing'}
      </Text>
    </Flex>
  );
}
function Example() {
  let [isOpen, setOpen] =
    React.useState(
      false
    );

  return (
    <Flex
      alignItems="center"
      gap="size-100"
    >
      <TooltipTrigger
        isOpen={isOpen}
        onOpenChange={setOpen}
      >
        <ActionButton aria-label="Resize">
          <Resize />
        </ActionButton>
        <Tooltip>
          Resize text.
        </Tooltip>
      </TooltipTrigger>
      <Text>
        Tooltip is{' '}
        {isOpen
          ? 'showing'
          : 'not showing'}
      </Text>
    </Flex>
  );
}

Props#


TooltipTrigger Props#

NameTypeDefaultDescription
children[ ReactElement, ReactElement ]
offsetnumber7

The additional offset applied along the main axis between the element and its anchor element.

placementPlacement'top'The placement of the tooltip with respect to the trigger.
isDisabledbooleanWhether the tooltip should be disabled, independent from the trigger.
delaynumber1500The delay time for the tooltip to show up. See guidelines.
trigger'focus'By default, opens for both focus and hover. Can be made to open only for focus.
isOpenbooleanWhether the overlay is open by default (controlled).
defaultOpenbooleanWhether the overlay is open by default (uncontrolled).
containerPaddingnumber12

The placement padding that should be applied between the element and its surrounding container.

crossOffsetnumber0

The additional offset applied along the cross axis between the element and its anchor element.

shouldFlipbooleantrue

Whether the element should flip its orientation (e.g. top to bottom or left to right) when there is insufficient room for it to render completely.

Events
NameTypeDescription
onOpenChange( (isOpen: boolean )) => voidHandler that is called when the overlay's open state changes.

Tooltip Props#

NameTypeDefaultDescription
childrenReactNode
variant'neutral''positive''negative''info'The visual style of the Tooltip.
placement'start''end''right''left''top''bottom''top'The placement of the element with respect to its anchor element.
showIconbooleanWhether the element is rendered.
isOpenboolean
idstringundefinedThe element's unique identifier. See MDN.
Layout
NameTypeDescription
flexResponsive<stringnumberboolean>When used in a flex layout, specifies how the element will grow or shrink to fit the space available. See MDN.
flexGrowResponsive<number>When used in a flex layout, specifies how the element will grow to fit the space available. See MDN.
flexShrinkResponsive<number>When used in a flex layout, specifies how the element will shrink to fit the space available. See MDN.
flexBasisResponsive<numberstring>When used in a flex layout, specifies the initial main size of the element. See MDN.
alignSelfResponsive<'auto''normal''start''end''center''flex-start''flex-end''self-start''self-end''stretch'>Overrides the alignItems property of a flex or grid container. See MDN.
justifySelfResponsive<'auto''normal''start''end''flex-start''flex-end''self-start''self-end''center''left''right''stretch'>Specifies how the element is justified inside a flex or grid container. See MDN.
orderResponsive<number>The layout order for the element within a flex or grid container. See MDN.
gridAreaResponsive<string>When used in a grid layout, specifies the named grid area that the element should be placed in within the grid. See MDN.
gridColumnResponsive<string>When used in a grid layout, specifies the column the element should be placed in within the grid. See MDN.
gridRowResponsive<string>When used in a grid layout, specifies the row the element should be placed in within the grid. See MDN.
gridColumnStartResponsive<string>When used in a grid layout, specifies the starting column to span within the grid. See MDN.
gridColumnEndResponsive<string>When used in a grid layout, specifies the ending column to span within the grid. See MDN.
gridRowStartResponsive<string>When used in a grid layout, specifies the starting row to span within the grid. See MDN.
gridRowEndResponsive<string>When used in a grid layout, specifies the ending row to span within the grid. See MDN.
Spacing
NameTypeDescription
marginResponsive<DimensionValue>The margin for all four sides of the element. See MDN.
marginTopResponsive<DimensionValue>The margin for the top side of the element. See MDN.
marginBottomResponsive<DimensionValue>The margin for the bottom side of the element. See MDN.
marginStartResponsive<DimensionValue>The margin for the logical start side of the element, depending on layout direction. See MDN.
marginEndResponsive<DimensionValue>The margin for the logical end side of an element, depending on layout direction. See MDN.
marginXResponsive<DimensionValue>The margin for both the left and right sides of the element. See MDN.
marginYResponsive<DimensionValue>The margin for both the top and bottom sides of the element. See MDN.
Sizing
NameTypeDescription
widthResponsive<DimensionValue>The width of the element. See MDN.
minWidthResponsive<DimensionValue>The minimum width of the element. See MDN.
maxWidthResponsive<DimensionValue>The maximum width of the element. See MDN.
heightResponsive<DimensionValue>The height of the element. See MDN.
minHeightResponsive<DimensionValue>The minimum height of the element. See MDN.
maxHeightResponsive<DimensionValue>The maximum height of the element. See MDN.
Positioning
NameTypeDescription
positionResponsive<'static''relative''absolute''fixed''sticky'>Specifies how the element is positioned. See MDN.
topResponsive<DimensionValue>The top position for the element. See MDN.
bottomResponsive<DimensionValue>The bottom position for the element. See MDN.
leftResponsive<DimensionValue>The left position for the element. See MDN. Consider using start instead for RTL support.
rightResponsive<DimensionValue>The right position for the element. See MDN. Consider using start instead for RTL support.
startResponsive<DimensionValue>The logical start position for the element, depending on layout direction. See MDN.
endResponsive<DimensionValue>The logical end position for the element, depending on layout direction. See MDN.
zIndexResponsive<number>The stacking order for the element. See MDN.
isHiddenResponsive<boolean>Hides the element.
Accessibility
NameTypeDescription
aria-labelstringDefines a string value that labels the current element.
aria-labelledbystringIdentifies the element (or elements) that labels the current element.
aria-describedbystringIdentifies the element (or elements) that describes the object.
aria-detailsstringIdentifies the element (or elements) that provide a detailed, extended description for the object.
Advanced
NameTypeDescription
UNSAFE_classNamestringSets the CSS className for the element. Only use as a last resort. Use style props instead.
UNSAFE_styleCSSPropertiesSets inline style for the element. Only use as a last resort. Use style props instead.

Visual options#


View guidelines

Positive

<TooltipTrigger>
  <ActionButton aria-label="Approve"><ThumbUp /></ActionButton>
  <Tooltip variant="positive" showIcon>Approve workflow.</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
  <ActionButton aria-label="Approve">
    <ThumbUp />
  </ActionButton>
  <Tooltip variant="positive" showIcon>
    Approve workflow.
  </Tooltip>
</TooltipTrigger>
<TooltipTrigger>
  <ActionButton aria-label="Approve">
    <ThumbUp />
  </ActionButton>
  <Tooltip
    variant="positive"
    showIcon
  >
    Approve workflow.
  </Tooltip>
</TooltipTrigger>

Information

<TooltipTrigger>
  <ActionButton aria-label="Information"><Question /></ActionButton>
  <Tooltip variant="info" showIcon>More information menu.</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
  <ActionButton aria-label="Information">
    <Question />
  </ActionButton>
  <Tooltip variant="info" showIcon>
    More information menu.
  </Tooltip>
</TooltipTrigger>
<TooltipTrigger>
  <ActionButton aria-label="Information">
    <Question />
  </ActionButton>
  <Tooltip
    variant="info"
    showIcon
  >
    More information
    menu.
  </Tooltip>
</TooltipTrigger>

Negative

<TooltipTrigger>
  <ActionButton aria-label="Danger Will Robinson"><Delete /></ActionButton>
  <Tooltip variant="negative" showIcon>Dangerous action.</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
  <ActionButton aria-label="Danger Will Robinson">
    <Delete />
  </ActionButton>
  <Tooltip variant="negative" showIcon>
    Dangerous action.
  </Tooltip>
</TooltipTrigger>
<TooltipTrigger>
  <ActionButton aria-label="Danger Will Robinson">
    <Delete />
  </ActionButton>
  <Tooltip
    variant="negative"
    showIcon
  >
    Dangerous action.
  </Tooltip>
</TooltipTrigger>

Options#


A TooltipTrigger can be disabled without disabling the trigger it displays on.

isDisabled

<TooltipTrigger isDisabled>
  <ActionButton
    aria-label="Danger Will Robinson"
    onPress={() => alert('pressed trigger')}
  >
    <Delete />
  </ActionButton>
  <Tooltip variant="negative" showIcon>Dangerous action.</Tooltip>
</TooltipTrigger>
<TooltipTrigger isDisabled>
  <ActionButton
    aria-label="Danger Will Robinson"
    onPress={() => alert('pressed trigger')}
  >
    <Delete />
  </ActionButton>
  <Tooltip variant="negative" showIcon>
    Dangerous action.
  </Tooltip>
</TooltipTrigger>
<TooltipTrigger
  isDisabled
>
  <ActionButton
    aria-label="Danger Will Robinson"
    onPress={() =>
      alert(
        'pressed trigger'
      )}
  >
    <Delete />
  </ActionButton>
  <Tooltip
    variant="negative"
    showIcon
  >
    Dangerous action.
  </Tooltip>
</TooltipTrigger>

Usage on disabled or non-interactive elements#


Tooltips need to be accessible to keyboard and screen reader users, so we ensure that they are only placed on focusable and hoverable elements. For example, plain text and disabled buttons aren't focusable, meaning keyboard and screen reader users would be unable to access the information in that tooltip.

If you need to display some additional context, consider using ContextualHelp.

import {Content, ContextualHelp, Flex, Heading} from '@adobe/react-spectrum';

<Flex gap="size-100" alignItems="center">
  <TooltipTrigger>
    <ActionButton isDisabled>Delete resource</ActionButton>
    <Tooltip variant="negative" showIcon>Dangerous action.</Tooltip>
  </TooltipTrigger>
  <ContextualHelp variant="info">
    <Heading>Permission required</Heading>
    <Content>
      Your admin must grant you permission before you can delete resources.
    </Content>
  </ContextualHelp>
</Flex>
import {
  Content,
  ContextualHelp,
  Flex,
  Heading
} from '@adobe/react-spectrum';

<Flex gap="size-100" alignItems="center">
  <TooltipTrigger>
    <ActionButton isDisabled>
      Delete resource
    </ActionButton>
    <Tooltip variant="negative" showIcon>
      Dangerous action.
    </Tooltip>
  </TooltipTrigger>
  <ContextualHelp variant="info">
    <Heading>Permission required</Heading>
    <Content>
      Your admin must grant you permission before you can
      delete resources.
    </Content>
  </ContextualHelp>
</Flex>
import {
  Content,
  ContextualHelp,
  Flex,
  Heading
} from '@adobe/react-spectrum';

<Flex
  gap="size-100"
  alignItems="center"
>
  <TooltipTrigger>
    <ActionButton
      isDisabled
    >
      Delete resource
    </ActionButton>
    <Tooltip
      variant="negative"
      showIcon
    >
      Dangerous action.
    </Tooltip>
  </TooltipTrigger>
  <ContextualHelp variant="info">
    <Heading>
      Permission
      required
    </Heading>
    <Content>
      Your admin must
      grant you
      permission before
      you can delete
      resources.
    </Content>
  </ContextualHelp>
</Flex>

Testing#


The TooltipTrigger features an popover that transitions in and out of the page as it is opened and closed. It also has a warmup and cooldown time that you've have to account for when interacting with it. Please see the following sections in the testing docs for more information on how to handle these behaviors in your test suite.

Timers

Please also refer to React Spectrum's test suite if you find that the above isn't sufficient when resolving issues in your own test cases.