Documentation Index Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
Use this file to discover all available pages before exploring further.
Gesture modifiers attach interaction handlers to any component. onTapGesture runs on a single (or multi-) tap; onDragGesture reports drag translation and velocity through every phase; onLongPressGesture fires after a press-and-hold; onHover tracks pointer enter/leave on web and desktop.
For interactive controls, prefer Button directly — gesture modifiers are best for adding handling to otherwise non-interactive components like Text, Image, and shapes.
onTapGesture
Runs an action when the component is tapped.
. onTapGesture ( action : ( locationInView : Point ) => void ): Component
. onTapGesture ( props : { count: number }, action : ( locationInView : Point ) => void ): Component
action
(locationInView: Point) => void
required
A callback that receives the tap location as a Point ({ x, y }) in the component’s coordinate space.
Configuration options for the tap gesture. The number of taps required to trigger the action (e.g., 2 for double-tap).
Single tap
Text ( "Tap me" )
. onTapGesture (( location ) => {
console . log ( "Tapped at: " + location . x + ", " + location . y )
})
Simple tap action (ignoring location)
const body = () => {
const [ count , setCount ] = useState ( 0 )
return Text ( "Tapped " + count + " times" )
. padding ( 16 )
. background ( Color ( "blue" ). opacity ( 0.1 ))
. cornerRadius ( 8 )
. onTapGesture (() => setCount ( count + 1 ))
}
Double tap
const body = () => {
const [ zoomed , setZoomed ] = useState ( false )
return Image ({ url: "photo.jpg" })
. resizable ()
. scaledToFit ()
. scaleEffect ( zoomed ? 2 : 1 )
. onTapGesture ({ count: 2 }, () => setZoomed ( ! zoomed ))
}
Combining single and double tap
const body = () => {
const [ label , setLabel ] = useState ( "Tap or double tap" )
return Text ( label )
. padding ( 16 )
. onTapGesture ({ count: 2 }, () => setLabel ( "Double tapped!" ))
. onTapGesture (() => setLabel ( "Single tapped!" ))
}
The locationInView is in the tapped component’s local coordinate space, with { x: 0, y: 0 } at the top-leading corner. When combining single and multi-tap gestures, apply the higher count gesture first — the system waits briefly to distinguish between single and multi-tap. For interactive controls like buttons, prefer using Button directly.
onDragGesture
Tracks drag gestures with translation and velocity.
. onDragGesture ( action : ( state : DragGestureState ) => void ): Component
. onDragGesture ( props : { minimumDistance? : number }, action : ( state : DragGestureState ) => void ): Component
action
(state: DragGestureState) => void
required
A callback that receives the drag gesture state throughout the gesture lifecycle. phase
"possible" | "began" | "changed" | "ended" | "cancelled"
The current phase of the gesture.
Touch/pointer location in the component’s coordinate space ({ x, y }).
Cumulative translation from the drag start point ({ x, y }).
Current drag velocity in points per second ({ x, y }).
Configuration options for the drag gesture. The minimum drag distance in points before the gesture is recognized.
Basic drag tracking
const body = () => {
const [ offset , setOffset ] = useState ({ x: 0 , y: 0 })
return Circle ()
. frame ({ width: 60 , height: 60 })
. foregroundStyle ( Color ( "blue" ))
. offset ( offset )
. onDragGesture (( state ) => {
if ( state . phase === "changed" ) {
setOffset ({
x: state . translation . x ,
y: state . translation . y
})
}
if ( state . phase === "ended" ) {
setOffset ({ x: 0 , y: 0 })
}
})
}
With minimum distance
Require a longer drag before recognition to avoid interfering with taps.
const body = () => {
const [ dragging , setDragging ] = useState ( false )
return Rectangle ()
. frame ({ width: 100 , height: 100 })
. foregroundStyle ( dragging ? Color ( "red" ) : Color ( "blue" ))
. onDragGesture ({ minimumDistance: 20 }, ( state ) => {
setDragging (
state . phase === "began" ||
state . phase === "changed"
)
})
}
Swipe detection using velocity
const body = () => {
const [ direction , setDirection ] = useState ( "" )
return Text ( direction || "Swipe me" )
. frame ({ width: 200 , height: 200 })
. background ( Color ( "gray" ). opacity ( 0.2 ))
. onDragGesture (( state ) => {
if ( state . phase === "ended" ) {
if ( state . velocity . x > 500 ) {
setDirection ( "Swiped right" )
} else if ( state . velocity . x < - 500 ) {
setDirection ( "Swiped left" )
}
}
})
}
The gesture callback fires for every phase: "possible", "began", "changed", "ended", and "cancelled". translation is cumulative from the initial touch point, not a per-frame delta. velocity is in points per second and is useful for flick/swipe detection.
onLongPressGesture
Tracks long press gestures.
. onLongPressGesture ( action : ( state : GestureState ) => void ): Component
. onLongPressGesture ( props : { minimumDuration? : number ; maximumDistance ?: number }, action : ( state : GestureState ) => void ): Component
action
(state: GestureState) => void
required
A callback that receives the gesture state throughout the gesture lifecycle. phase
"possible" | "began" | "changed" | "ended" | "cancelled"
The current phase of the gesture.
Touch/pointer location in the component’s coordinate space ({ x, y }).
Configuration options for the long press gesture. The minimum press duration in seconds before the gesture is recognized.
The maximum distance in points the finger can move before the gesture is cancelled.
Basic long press
const body = () => {
const [ pressed , setPressed ] = useState ( false )
return Text ( pressed ? "Long pressed!" : "Press and hold" )
. padding ( 16 )
. background ( Color ( "gray" ). opacity ( 0.2 ))
. cornerRadius ( 8 )
. onLongPressGesture (( state ) => {
if ( state . phase === "ended" ) {
setPressed ( true )
}
})
}
Custom duration
Require a 2-second press.
Text ( "Hold for 2 seconds" )
. padding ( 16 )
. onLongPressGesture ({ minimumDuration: 2 }, ( state ) => {
if ( state . phase === "ended" ) {
console . log ( "Long press recognized" )
}
})
Visual feedback during press
const body = () => {
const [ pressing , setPressing ] = useState ( false )
return Circle ()
. frame ({ width: 80 , height: 80 })
. foregroundStyle ( pressing ? Color ( "red" ) : Color ( "blue" ))
. scaleEffect ( pressing ? 0.9 : 1 )
. onLongPressGesture (( state ) => {
setPressing (
state . phase === "began" ||
state . phase === "changed"
)
})
}
The gesture callback fires for every phase: "possible", "began", "changed", "ended", and "cancelled". Use "began" and "changed" to show press-in-progress feedback, and "ended" to perform the action. If the finger moves beyond maximumDistance, the gesture is cancelled.
onHover
Fires when the pointer hovers over or leaves the component.
. onHover ( action : ( isHovering : boolean ) => void ): Component
action
(isHovering: boolean) => void
required
A callback that receives true when the pointer enters the component and false when it leaves.
Hover highlight
const body = () => {
const [ hovered , setHovered ] = useState ( false )
return Text ( "Hover me" )
. padding ( 16 )
. background ( hovered ? Color ( "blue" ). opacity ( 0.1 ) : Color ( "clear" ))
. cornerRadius ( 8 )
. onHover (( isHovering ) => setHovered ( isHovering ))
}
Changing text on hover
const body = () => {
const [ hovered , setHovered ] = useState ( false )
return Text ( hovered ? "Hovered!" : "Hover me" )
. foregroundStyle ( hovered ? Color ( "blue" ) : Color ( "gray" ))
. onHover (( isHovering ) => setHovered ( isHovering ))
}
Scale effect on hover
const body = () => {
const [ hovered , setHovered ] = useState ( false )
return Image ({ url: "thumbnail.jpg" })
. resizable ()
. frame ({ width: 100 , height: 100 })
. scaleEffect ( hovered ? 1.05 : 1 )
. onHover (( isHovering ) => setHovered ( isHovering ))
}
This modifier is primarily useful for web and desktop pointer interactions. On touch-only devices, hover events do not occur. Currently only supported on the web platform.
See also