A lightweight, performant drag and drop library specifically designed for Ionic React applications with Capacitor support. Features include auto-scrolling that works with IonContent's shadow DOM, haptic feedback on mobile, and smooth animations.
- 🚀 Optimized for Ionic - Works seamlessly with IonContent, IonList, and other Ionic components
- 📱 Capacitor Ready - Built-in haptic feedback and touch optimization for mobile apps
- 🎨 Tailwind Friendly - Designed to work with Tailwind CSS classes
- 📜 Auto-Scroll - Intelligent auto-scrolling that works with IonContent's shadow DOM
- ⚡ Performant - Uses CSS transforms and requestAnimationFrame for smooth 60fps animations
- 🎯 TypeScript First - Full TypeScript support with comprehensive types
- 🔧 Flexible - Use pre-built components or hooks for custom implementations
npm install @oyfora/ionic-dnd
# or
yarn add @oyfora/ionic-dnd
# or
pnpm add @oyfora/ionic-dndnpm install react react-dom @ionic/reactnpm install @capacitor/haptics
npx cap syncimport React, { useState } from "react";
import { IonContent, IonList, IonPage } from "@ionic/react";
import {
arrayMove,
DragDropProvider,
DragEndEvent,
SortableItem,
} from "@oyfora/ionic-dnd";
function SortableList() {
const [items, setItems] = useState([
{ id: "1", name: "Item 1" },
{ id: "2", name: "Item 2" },
{ id: "3", name: "Item 3" },
]);
const handleDragEnd = (event: DragEndEvent) => {
if (!event.cancelled && event.fromIndex !== event.toIndex) {
setItems(arrayMove(items, event.fromIndex, event.toIndex));
}
};
return (
<IonPage>
<IonContent>
<DragDropProvider onDragEnd={handleDragEnd}>
<IonList>
{items.map((item, index) => (
<SortableItem
key={item.id}
id={item.id}
index={index}
className="p-4 bg-white border-b"
>
{item.name}
</SortableItem>
))}
</IonList>
</DragDropProvider>
</IonContent>
</IonPage>
);
}import React from "react";
import { useSortable } from "@oyfora/ionic-dnd";
interface MyItemProps {
id: string;
index: number;
children: React.ReactNode;
}
function MyItem({ id, index, children }: MyItemProps) {
const {
ref,
handleRef,
isDragging,
isOver,
transform,
transition,
attributes,
listeners,
} = useSortable({ id, index });
const style: React.CSSProperties = {
transform: transform
? `translate3d(${transform.x}px, ${transform.y}px, 0)`
: undefined,
transition,
opacity: isDragging ? 0.5 : 1,
touchAction: "none",
};
return (
<div
ref={ref}
style={style}
className={`p-4 rounded-lg ${isDragging ? "shadow-xl" : ""}`}
{...attributes}
{...listeners}
>
{/* Optional: Separate drag handle */}
<div ref={handleRef} className="cursor-grab">
☰
</div>
{children}
</div>
);
}import {
arrayMove,
DragDropProvider,
SortableContainer,
SortableItem,
} from "@oyfora/ionic-dnd";
function SortableGrid() {
const [items, setItems] = useState(["1", "2", "3", "4", "5", "6"]);
return (
<DragDropProvider
onDragEnd={(e) => {
if (!e.cancelled) {
setItems(arrayMove(items, e.fromIndex, e.toIndex));
}
}}
>
<SortableContainer
items={items}
strategy="grid"
columns={3}
className="gap-4 p-4"
>
{items.map((id, index) => (
<SortableItem
key={id}
id={id}
index={index}
className="aspect-square bg-blue-500 rounded-lg flex items-center justify-center text-white"
>
{id}
</SortableItem>
))}
</SortableContainer>
</DragDropProvider>
);
}<DragDropProvider
config={{
// Auto-scroll settings
autoScroll: {
enabled: true, // Enable/disable auto-scroll
threshold: 80, // Pixels from edge to start scrolling
maxSpeed: 15, // Maximum scroll speed
acceleration: 1.5, // Speed acceleration factor
},
// Activation settings
activationDelay: 150, // ms delay before drag starts (prevents accidental drags)
activationDistance: 5, // pixels to move before drag starts
// Features
hapticFeedback: true, // Enable haptic feedback on Capacitor
lockAxis: null, // Lock to 'x', 'y', or null for free movement
}}
onDragStart={(event) => console.log("Started", event)}
onDragMove={(event) => console.log("Moving", event)}
onDragEnd={(event) => console.log("Ended", event)}
onDragOver={(event) => console.log("Over", event)}
>
{/* Your sortable content */}
</DragDropProvider>;const sortable = useSortable({
id: "unique-id", // Required: unique identifier
index: 0, // Required: position in list
disabled: false, // Optional: disable dragging
transitionDuration: 200, // Optional: animation duration in ms
});Main hook for creating sortable items.
Returns:
ref- Ref to attach to the sortable elementhandleRef- Ref for optional drag handleisDragging- Whether this item is being draggedisOver- Whether another item is being dragged over this onetransform- Current transform offset{ x, y }transition- CSS transition stringattributes- Accessibility attributeslisteners- Event listeners to spread on the element
Monitor drag state without being a draggable.
Returns:
isDragging- Whether any drag is in progressdraggedId- ID of the dragged itemdraggedIndex- Index of the dragged itemoverIndex- Index being hovered overcurrentPosition- Current pointer position
Imperatively control drag operations.
Returns:
cancel()- Cancel the current dragcomplete()- Complete the current drag
Low-level hook for auto-scroll functionality.
Context provider for drag and drop functionality.
Container component for sortable items with layout options.
Pre-built sortable item component.
Overlay component for custom drag previews.
Reorder items in an array.
Insert an item at a specific index.
Remove an item at a specific index.
All types are exported and fully documented:
import type {
AutoScrollConfig,
DragDropContextConfig,
DragEndEvent,
DragMoveEvent,
DragOverEvent,
DragStartEvent,
UseSortableReturn,
} from "@oyfora/ionic-dnd";- Chrome/Edge 80+
- Safari 13+
- Firefox 75+
- iOS Safari 13+
- Chrome for Android 80+
MIT © Oyfora