Hover Card
A card that appears when a user hovers over an element.
Anatomy
To set up the hover card correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-part
attribute to help identify them in the DOM.
Examples
Learn how to use the HoverCard
component in your project. Let's take a look at the most basic example:
import { HoverCard } from '@ark-ui/react/hover-card'
import { Portal } from '@ark-ui/react/portal'
export const Basic = () => (
<HoverCard.Root>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.Root>
)
import { HoverCard } from '@ark-ui/solid/hover-card'
import { Portal } from 'solid-js/web'
export const Basic = () => (
<HoverCard.Root>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.Root>
)
<script setup lang="ts">
import { HoverCard } from '@ark-ui/vue/hover-card'
</script>
<template>
<HoverCard.Root>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Teleport to="body">
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Teleport>
</HoverCard.Root>
</template>
<script lang="ts">
import { HoverCard } from '@ark-ui/svelte/hover-card'
</script>
<HoverCard.Root>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</HoverCard.Root>
Controlled HoverCard
The controlled HoverCard
component provides an interface for managing the state of the hover card using the open
and
onOpenChange
props:
import { HoverCard } from '@ark-ui/react/hover-card'
import { Portal } from '@ark-ui/react/portal'
import { useState } from 'react'
export const Controlled = () => {
const [isOpen, setOpen] = useState(false)
return (
<>
<button type="button" onClick={() => setOpen(!isOpen)}>
click me
</button>
<HoverCard.Root open={isOpen} onOpenChange={() => setOpen(false)}>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.Root>
</>
)
}
import { HoverCard } from '@ark-ui/solid/hover-card'
import { createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
export const Controlled = () => {
const [isOpen, setOpen] = createSignal(false)
return (
<>
<button type="button" onClick={() => setOpen(!isOpen)}>
click me
</button>
<HoverCard.Root open={isOpen()} onOpenChange={() => setOpen(false)}>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.Root>
</>
)
}
<script setup lang="ts">
import { HoverCard } from '@ark-ui/vue/hover-card'
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<button @click="() => (open = true)">Open Dialog</button>
<HoverCard.Root v-model:open="open">
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Teleport to="body">
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Teleport>
</HoverCard.Root>
</template>
<script lang="ts">
import { HoverCard } from '@ark-ui/svelte/hover-card'
let open = $state(false)
</script>
<p>Open: {open ? 'true' : 'false'}</p>
<HoverCard.Root bind:open>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</HoverCard.Root>
Custom Positioning
The HoverCard
component can be customized in its placement and distance from the trigger element through the
positioning
prop:
import { HoverCard } from '@ark-ui/react/hover-card'
import { Portal } from '@ark-ui/react/portal'
export const Positioning = () => (
<HoverCard.Root positioning={{ placement: 'right', gutter: 12 }}>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.Root>
)
import { HoverCard } from '@ark-ui/solid/hover-card'
import { Portal } from 'solid-js/web'
export const Positioning = () => (
<HoverCard.Root positioning={{ placement: 'right', gutter: 12 }}>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.Root>
)
<script setup lang="ts">
import { HoverCard } from '@ark-ui/vue/hover-card'
</script>
<template>
<HoverCard.Root
:positioning="{
placement: 'right',
gutter: 12,
}"
>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Teleport to="body">
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Teleport>
</HoverCard.Root>
</template>
<script lang="ts">
import { HoverCard } from '@ark-ui/svelte/hover-card'
import { Portal } from '@ark-ui/svelte/portal'
</script>
<HoverCard.Root positioning={{ placement: 'right', gutter: 12 }}>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.Root>
Render Prop Usage
The HoverCard
component can also accept a render prop, giving the user more control over rendering behavior. This is
useful for dynamically updating the trigger based on the state of the HoverCard
:
import { HoverCard } from '@ark-ui/react/hover-card'
import { Portal } from '@ark-ui/react/portal'
export const RenderProp = () => (
<HoverCard.Root>
<HoverCard.Context>
{(hoverCard) => <HoverCard.Trigger>Hover me {hoverCard.open ? '▲' : '▼'} </HoverCard.Trigger>}
</HoverCard.Context>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.Root>
)
import { HoverCard } from '@ark-ui/solid/hover-card'
import { Portal } from 'solid-js/web'
export const RenderProp = () => (
<HoverCard.Root>
<HoverCard.Context>
{(context) => <HoverCard.Trigger>Hover me {context().open ? '▲' : '▼'} </HoverCard.Trigger>}
</HoverCard.Context>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.Root>
)
<script setup lang="ts">
import { HoverCard } from '@ark-ui/vue/hover-card'
</script>
<template>
<HoverCard.Root>
<HoverCard.Context v-slot="hoverCard">
<HoverCard.Trigger>Hover me {{ hoverCard.open ? '▲' : '▼' }}</HoverCard.Trigger>
</HoverCard.Context>
<Teleport to="body">
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Teleport>
</HoverCard.Root>
</template>
<script lang="ts">
import { HoverCard } from '@ark-ui/svelte/hover-card'
import { Portal } from '@ark-ui/svelte/portal'
</script>
<HoverCard.Root>
<HoverCard.Context>
{#snippet render(hoverCard)}
<HoverCard.Trigger>Hover me {hoverCard().open ? '▲' : '▼'}</HoverCard.Trigger>
{/snippet}
</HoverCard.Context>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.Root>
Using the Root Provider
The RootProvider
component provides a context for the hover-card. It accepts the value of the useHover-card
hook.
You can leverage it to access the component state and methods from outside the hover-card.
import { HoverCard, useHoverCard } from '@ark-ui/react/hover-card'
import { Portal } from '@ark-ui/react/portal'
export const RootProvider = () => {
const hoverCard = useHoverCard()
return (
<>
<button onClick={() => hoverCard.setOpen(true)}>Open</button>
<HoverCard.RootProvider value={hoverCard}>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.RootProvider>
</>
)
}
import { HoverCard, useHoverCard } from '@ark-ui/solid/hover-card'
import { Portal } from 'solid-js/web'
export const RootProvider = () => {
const hoverCard = useHoverCard()
return (
<>
<button onClick={() => hoverCard().setOpen(true)}>Open</button>
<HoverCard.RootProvider value={hoverCard}>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.RootProvider>
</>
)
}
<script setup lang="ts">
import { HoverCard, useHoverCard } from '@ark-ui/vue/hover-card'
const hoverCard = useHoverCard()
</script>
<template>
<button @click="hoverCard.setOpen(true)">Open</button>
<HoverCard.RootProvider :value="hoverCard">
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Teleport to="body">
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Teleport>
</HoverCard.RootProvider>
</template>
<script lang="ts">
import { HoverCard, useHoverCard } from '@ark-ui/svelte/hover-card'
import { Portal } from '@ark-ui/svelte/portal'
const id = $props.id()
const hoverCard = useHoverCard({ id })
</script>
<button type="button" onclick={() => hoverCard().setOpen(true)}>Open</button>
<HoverCard.RootProvider value={hoverCard}>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<Portal>
<HoverCard.Positioner>
<HoverCard.Content>
<HoverCard.Arrow>
<HoverCard.ArrowTip />
</HoverCard.Arrow>
Content
</HoverCard.Content>
</HoverCard.Positioner>
</Portal>
</HoverCard.RootProvider>
If you're using the
RootProvider
component, you don't need to use theRoot
component.
API Reference
Root
Prop | Default | Type |
---|---|---|
closeDelay | 300 | number The duration from when the mouse leaves the trigger or content until the hover card closes. |
defaultOpen | boolean The initial open state of the hover card when rendered. Use when you don't need to control the open state of the hover card. | |
id | string The unique identifier of the machine. | |
ids | Partial<{ trigger: string; content: string; positioner: string; arrow: string }> The ids of the elements in the popover. Useful for composition. | |
immediate | boolean Whether to synchronize the present change immediately or defer it to the next frame | |
lazyMount | false | boolean Whether to enable lazy mounting |
onExitComplete | VoidFunction Function called when the animation ends in the closed state | |
onFocusOutside | (event: FocusOutsideEvent) => void Function called when the focus is moved outside the component | |
onInteractOutside | (event: InteractOutsideEvent) => void Function called when an interaction happens outside the component | |
onOpenChange | (details: OpenChangeDetails) => void Function called when the hover card opens or closes. | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => void Function called when the pointer is pressed down outside the component | |
open | boolean The controlled open state of the hover card | |
openDelay | 700 | number The duration from when the mouse enters the trigger until the hover card opens. |
positioning | PositioningOptions The user provided options used to position the popover content | |
present | boolean Whether the node is present (controlled by the user) | |
skipAnimationOnMount | false | boolean Whether to allow the initial presence animation. |
unmountOnExit | false | boolean Whether to unmount on exit. |
Arrow
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
ArrowTip
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Content
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Data Attribute | Value |
---|---|
[data-scope] | hover-card |
[data-part] | content |
[data-state] | "open" | "closed" |
[data-placement] | The placement of the content |
Positioner
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
RootProvider
Prop | Default | Type |
---|---|---|
value | UseHoverCardReturn | |
immediate | boolean Whether to synchronize the present change immediately or defer it to the next frame | |
lazyMount | false | boolean Whether to enable lazy mounting |
onExitComplete | VoidFunction Function called when the animation ends in the closed state | |
present | boolean Whether the node is present (controlled by the user) | |
skipAnimationOnMount | false | boolean Whether to allow the initial presence animation. |
unmountOnExit | false | boolean Whether to unmount on exit. |
Trigger
Prop | Default | Type |
---|---|---|
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Data Attribute | Value |
---|---|
[data-scope] | hover-card |
[data-part] | trigger |
[data-placement] | The placement of the trigger |
[data-state] | "open" | "closed" |