A production-ready motion library for React that provides comprehensive animation and gesture APIs for creating fluid, performant interactions.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Drag controls and gesture event handlers for interactive animations and user input handling.
Creates controls for programmatically managing drag operations.
/**
* Creates drag controls for manual drag management
* @returns DragControls instance
*/
function useDragControls(): DragControls;
interface DragControls {
/**
* Start a drag operation
* @param event - Pointer event that triggered the drag
* @param options - Drag start options
*/
start(event: React.PointerEvent | PointerEvent, options?: StartOptions): void;
/**
* Cancel the current drag operation
*/
cancel(): void;
/**
* Stop the current drag operation
*/
stop(): void;
/**
* Subscribe to drag controls updates
* @param controls - Another drag controls instance to link
*/
subscribe(controls: DragControls): () => void;
}
interface StartOptions {
/**
* Snap back to original position when drag ends
*/
snapToCursor?: boolean;
/**
* Cursor to use during drag
*/
cursorProgress?: Point;
}
interface Point {
x: number;
y: number;
}Usage Examples:
import { motion, useDragControls } from "framer-motion";
import { useRef } from "react";
// Basic drag controls
function DragControlsExample() {
const controls = useDragControls();
return (
<div>
<div
className="w-8 h-8 bg-blue-500 cursor-grab rounded-full mb-4"
onPointerDown={(e) => controls.start(e)}
>
Drag Handle
</div>
<motion.div
drag
dragControls={controls}
className="w-24 h-24 bg-red-500 rounded"
>
Draggable Element
</motion.div>
</div>
);
}
// Multiple linked drag controls
function LinkedDragExample() {
const controls1 = useDragControls();
const controls2 = useDragControls();
// Link controls so they move together
React.useEffect(() => {
const unsubscribe = controls1.subscribe(controls2);
return unsubscribe;
}, [controls1, controls2]);
return (
<div className="space-y-4">
<motion.div
drag
dragControls={controls1}
className="w-24 h-24 bg-blue-500 rounded"
>
Element 1
</motion.div>
<motion.div
drag
dragControls={controls2}
className="w-24 h-24 bg-green-500 rounded"
>
Element 2 (linked)
</motion.div>
</div>
);
}Motion components support comprehensive drag functionality through props.
interface DragProps {
/**
* Enable dragging on x, y, or both axes
*/
drag?: boolean | "x" | "y";
/**
* Drag controls instance for manual control
*/
dragControls?: DragControls;
/**
* Constraints for drag boundaries
*/
dragConstraints?: Partial<BoundingBox2D> | React.RefObject<Element>;
/**
* Enable elastic drag beyond constraints
*/
dragElastic?: boolean | number;
/**
* Enable momentum and inertia after drag ends
*/
dragMomentum?: boolean;
/**
* Transition for snapping back to constraints
*/
dragTransition?: {
bounceStiffness?: number;
bounceDamping?: number;
power?: number;
timeConstant?: number;
restDelta?: number;
modifyTarget?: (target: number) => number;
};
/**
* Propagate drag to parent elements
*/
dragPropagation?: boolean;
/**
* Listen for external drag events
*/
dragListener?: boolean;
/**
* Direction lock threshold
*/
dragDirectionLock?: boolean;
/**
* Minimum distance before drag starts
*/
dragSnapToOrigin?: boolean;
}
interface BoundingBox2D {
top: number;
right: number;
bottom: number;
left: number;
}Drag Event Handlers:
interface DragEventHandlers {
/**
* Called when drag operation starts
*/
onDragStart?: (
event: MouseEvent | TouchEvent | PointerEvent,
info: PanInfo
) => void;
/**
* Called continuously during drag
*/
onDrag?: (
event: MouseEvent | TouchEvent | PointerEvent,
info: PanInfo
) => void;
/**
* Called when drag operation ends
*/
onDragEnd?: (
event: MouseEvent | TouchEvent | PointerEvent,
info: PanInfo
) => void;
/**
* Called when dragging beyond constraints (elastic)
*/
onDirectionLock?: (axis: "x" | "y") => void;
}
interface PanInfo {
/**
* Current pointer position relative to page
*/
point: Point;
/**
* Change in position since last event
*/
delta: Point;
/**
* Total offset from drag start position
*/
offset: Point;
/**
* Current velocity of the pointer
*/
velocity: Point;
}Usage Examples:
import { motion } from "framer-motion";
import { useRef, useState } from "react";
// Constrained dragging
function ConstrainedDragExample() {
const constraintsRef = useRef<HTMLDivElement>(null);
return (
<div
ref={constraintsRef}
className="w-96 h-96 bg-gray-200 relative border-2 border-gray-400"
>
<motion.div
drag
dragConstraints={constraintsRef}
dragElastic={0.2}
className="w-16 h-16 bg-blue-500 rounded cursor-grab"
>
Constrained
</motion.div>
</div>
);
}
// Axis-specific dragging with momentum
function AxisDragExample() {
return (
<div className="space-y-8">
<motion.div
drag="x"
dragMomentum={true}
dragConstraints={{ left: 0, right: 300 }}
className="w-16 h-16 bg-red-500 rounded cursor-grab"
>
X-axis only
</motion.div>
<motion.div
drag="y"
dragMomentum={false}
dragConstraints={{ top: 0, bottom: 200 }}
className="w-16 h-16 bg-green-500 rounded cursor-grab"
>
Y-axis only
</motion.div>
</div>
);
}
// Drag with custom transitions and events
function AdvancedDragExample() {
const [dragInfo, setDragInfo] = useState<PanInfo | null>(null);
return (
<div>
<motion.div
drag
dragConstraints={{ left: -200, right: 200, top: -200, bottom: 200 }}
dragTransition={{
bounceStiffness: 300,
bounceDamping: 20,
power: 0.2,
modifyTarget: (target) => Math.round(target / 50) * 50 // Snap to grid
}}
onDragStart={(event, info) => {
console.log("Drag started", info);
}}
onDrag={(event, info) => {
setDragInfo(info);
}}
onDragEnd={(event, info) => {
console.log("Drag ended", info);
setDragInfo(null);
}}
className="w-16 h-16 bg-purple-500 rounded cursor-grab"
>
Advanced Drag
</motion.div>
{dragInfo && (
<div className="mt-4 text-sm">
<p>Position: {Math.round(dragInfo.point.x)}, {Math.round(dragInfo.point.y)}</p>
<p>Velocity: {Math.round(dragInfo.velocity.x)}, {Math.round(dragInfo.velocity.y)}</p>
<p>Offset: {Math.round(dragInfo.offset.x)}, {Math.round(dragInfo.offset.y)}</p>
</div>
)}
</div>
);
}Handle mouse hover interactions with motion components.
interface HoverEventHandlers {
/**
* Called when mouse enters element
*/
onHoverStart?: (event: MouseEvent, info: EventInfo) => void;
/**
* Called when mouse leaves element
*/
onHoverEnd?: (event: MouseEvent, info: EventInfo) => void;
}
interface EventInfo {
/**
* Current pointer position
*/
point: Point;
}Usage Example:
import { motion } from "framer-motion";
import { useState } from "react";
function HoverExample() {
const [hoverInfo, setHoverInfo] = useState<string>("");
return (
<motion.div
whileHover={{ scale: 1.1, rotate: 5 }}
onHoverStart={(event, info) => {
setHoverInfo(`Hover started at ${info.point.x}, ${info.point.y}`);
}}
onHoverEnd={(event, info) => {
setHoverInfo(`Hover ended at ${info.point.x}, ${info.point.y}`);
}}
className="w-32 h-32 bg-blue-500 rounded cursor-pointer flex items-center justify-center text-white"
>
Hover me
{hoverInfo && <div className="absolute top-full mt-2 text-sm text-black">{hoverInfo}</div>}
</motion.div>
);
}Handle tap/click interactions with detailed gesture information.
interface TapEventHandlers {
/**
* Called when tap/click occurs
*/
onTap?: (event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo) => void;
/**
* Called when tap/click starts (pointer down)
*/
onTapStart?: (event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo) => void;
/**
* Called when tap is cancelled (pointer moved too far)
*/
onTapCancel?: (event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo) => void;
}
interface TapInfo {
/**
* Current pointer position
*/
point: Point;
}Usage Example:
import { motion } from "framer-motion";
import { useState } from "react";
function TapExample() {
const [tapCount, setTapCount] = useState(0);
const [lastTapPoint, setLastTapPoint] = useState<Point | null>(null);
return (
<motion.button
whileTap={{ scale: 0.95 }}
onTap={(event, info) => {
setTapCount(count => count + 1);
setLastTapPoint(info.point);
}}
onTapStart={(event, info) => {
console.log("Tap started at", info.point);
}}
onTapCancel={(event, info) => {
console.log("Tap cancelled at", info.point);
}}
className="px-6 py-3 bg-green-500 text-white rounded hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-400"
>
Tap me (count: {tapCount})
{lastTapPoint && (
<div className="text-xs mt-1">
Last tap: {Math.round(lastTapPoint.x)}, {Math.round(lastTapPoint.y)}
</div>
)}
</motion.button>
);
}Handle focus and blur events for accessibility and keyboard navigation.
interface FocusEventHandlers {
/**
* Called when element receives focus
*/
onFocus?: (event: FocusEvent) => void;
/**
* Called when element loses focus
*/
onBlur?: (event: FocusEvent) => void;
}Usage Example:
import { motion } from "framer-motion";
import { useState } from "react";
function FocusExample() {
const [isFocused, setIsFocused] = useState(false);
return (
<motion.input
type="text"
placeholder="Focus me"
whileFocus={{ scale: 1.05, borderColor: "#3B82F6" }}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
className="px-4 py-2 border-2 border-gray-300 rounded outline-none transition-colors"
style={{
borderColor: isFocused ? "#3B82F6" : "#D1D5DB"
}}
/>
);
}Utilities for handling DOM events in a cross-platform way.
/**
* Add pointer event listener with proper cross-platform handling
* @param element - Element to add listener to
* @param event - Event type to listen for
* @param handler - Event handler function
* @param options - Event listener options
* @returns Cleanup function
*/
function addPointerEvent(
element: Element,
event: string,
handler: (event: PointerEvent) => void,
options?: AddEventListenerOptions
): () => void;
/**
* Add pointer info to event handlers for consistent data structure
* @param handler - Original event handler
* @returns Enhanced handler with pointer info
*/
function addPointerInfo<T extends Event>(
handler: (event: T, info: EventInfo) => void
): (event: T) => void;
/**
* React hook for DOM event handling
* @param ref - Element reference
* @param event - Event type
* @param handler - Event handler
* @param options - Event options
*/
function useDomEvent(
ref: React.RefObject<Element>,
event: string,
handler: (event: Event) => void,
options?: AddEventListenerOptions
): void;Usage Example:
import { useDomEvent } from "framer-motion";
import { useRef, useEffect } from "react";
function DOMEventExample() {
const ref = useRef<HTMLDivElement>(null);
// React hook approach
useDomEvent(ref, "pointerdown", (event) => {
console.log("Pointer down:", event);
});
// Manual approach
useEffect(() => {
const element = ref.current;
if (!element) return;
const cleanup = addPointerEvent(
element,
"pointermove",
(event) => {
console.log("Pointer move:", event.clientX, event.clientY);
},
{ passive: true }
);
return cleanup;
}, []);
return (
<div
ref={ref}
className="w-64 h-64 bg-gray-200 border-2 border-gray-400 flex items-center justify-center"
>
Interactive area
</div>
);
}Drag with Hover:
<motion.div
drag
whileHover={{ scale: 1.05 }}
whileDrag={{ scale: 1.1, zIndex: 10 }}
onDragStart={() => console.log("Started dragging")}
onHoverStart={() => console.log("Started hovering")}
>
Combined gestures
</motion.div>Conditional Gestures:
const [isDraggable, setIsDraggable] = useState(true);
<motion.div
drag={isDraggable}
dragConstraints={isDraggable ? { left: 0, right: 300 } : false}
whileTap={!isDraggable ? { scale: 0.95 } : undefined}
onTap={() => {
if (!isDraggable) {
console.log("Clicked instead of dragged");
}
}}
>
Conditional interaction
</motion.div>Multi-Touch Support:
<motion.div
drag
onPanStart={(event, info) => {
// Handle touch/pan start
console.log("Pan started with", info.point);
}}
onPan={(event, info) => {
// Handle continuous touch/pan
console.log("Panning:", info.delta);
}}
onPanEnd={(event, info) => {
// Handle touch/pan end
console.log("Pan ended with velocity:", info.velocity);
}}
>
Multi-touch support
</motion.div>