Beta Status
This library is still in early beta and the API is subject to change and may get daily breaking changes. The documentaton may not be up to date with the latest features and include missing or outdated information. You can always create an issue on GitHub or even better a pull request to fix the documentation or add new features.
Drag
Drag functionality is built into motion elements. Combine with whileDrag for visual feedback during drag operations.
Basic Drag
<motion.div drag />
Axis locking:
<motion.div drag="x" /> {/* Horizontal only */}
<motion.div drag="y" /> {/* Vertical only */}
Constraints
Limit drag range with a bounding box or element reference.
Element Constraints
let container!: HTMLDivElement;
<div ref={container} class="relative">
<motion.div drag dragConstraints={container} />
</div>;
Object Constraints
<motion.div
drag
dragConstraints={{
left: -100,
right: 100,
top: -50,
bottom: 50,
}}
/>
Options
dragElastic- How far past constraints (0-1), or per-edge objectdragMomentum- Enable inertia on release (default: true)dragDirectionLock- Lock to initial drag axis after thresholddragPropagation- Allow nested drags to activatedragSnapToOrigin- Snap back to start on releasedragTransition- Transition for release animationdragListener- Enable pointer listeners (default: true)
Elastic
// 0.5 = can stretch 50% past constraints
<motion.div drag dragConstraints={{ left: 0, right: 200 }} dragElastic={0.5} />
Snap to Origin
<motion.div drag dragSnapToOrigin />
Drag Controls
Control drag programmatically with createDragControls:
import { createDragControls } from "motion-solid";
function DraggableHandle() {
const controls = createDragControls();
return (
<>
<motion.div
drag
dragControls={controls}
dragListener={false}
class="draggable-box"
>
<div onPointerDown={(e) => controls.start(e)} class="drag-handle">
Drag here
</div>
</motion.div>
</>
);
}
Callbacks
<motion.div
drag
onDragStart={(e, info) => console.log("start", info.point)}
onDrag={(e, info) => console.log("drag", info.offset)}
onDragEnd={(e, info) => console.log("end", info.velocity)}
onDirectionLock={(axis) => console.log("locked to", axis)}
onDragTransitionEnd={() => console.log("momentum settled")}
/>
Drag info: point (current position), delta (movement since last frame), offset (total from start), velocity.
While Drag
Combine with whileDrag for visual feedback:
<motion.div
drag
dragConstraints={{ left: 0, right: 300 }}
whileDrag={{ scale: 1.1, "box-shadow": "0 8px 24px rgba(0,0,0,0.2)" }}
/>
Example: Slider
function Slider() {
const [value, setValue] = createSignal(50);
let track!: HTMLDivElement;
const handleDrag = (e, info) => {
const rect = track.getBoundingClientRect();
const percent = Math.max(
0,
Math.min(100, ((info.point.x - rect.left) / rect.width) * 100),
);
setValue(percent);
};
return (
<div ref={track} class="slider-track">
<motion.div
drag="x"
dragConstraints={track}
dragElastic={0}
onDrag={handleDrag}
class="slider-thumb"
/>
<div class="slider-fill" style={{ width: `${value()}%` }} />
</div>
);
}
Example: Bottom Sheet
function BottomSheet(props: { open: Accessor<boolean>; onClose: () => void }) {
const handleDragEnd = (e, info) => {
if (info.offset.y > 100 || info.velocity.y > 500) {
props.onClose();
}
};
return (
<Show when={props.open()}>
<motion.div
initial={{ y: "100%" }}
animate={{ y: 0 }}
exit={{ y: "100%" }}
drag="y"
dragConstraints={{ top: 0, bottom: 0 }}
dragElastic={{ top: 0, bottom: 0.5 }}
onDragEnd={handleDragEnd}
class="bottom-sheet"
>
<div class="handle" />
{props.children}
</motion.div>
</Show>
);
}
Notes
- Drag uses pointer events for cross-device support
- Touch devices: set
touch-action: noneon draggable elements to prevent scrolling - For nested draggables, use
dragPropagationto allow inner elements to activate