0
# DraggableCore Component
1
2
DraggableCore is a low-level, stateless React component that provides drag events without any position management or visual transforms. It gives complete control over drag behavior to the parent component, making it perfect for custom drag implementations, complex interactions, or when you need full control over positioning logic.
3
4
## Capabilities
5
6
### Core Component
7
8
The stateless draggable component that only provides drag events.
9
10
```typescript { .api }
11
/**
12
* A low-level draggable component with no internal state
13
* Provides drag events but no position management
14
* @param props - DraggableCore configuration properties
15
*/
16
function DraggableCore(props: DraggableCoreProps): React.ReactElement;
17
18
interface DraggableCoreProps {
19
// Interaction controls
20
handle?: string;
21
cancel?: string;
22
disabled?: boolean;
23
allowAnyClick?: boolean;
24
allowMobileScroll?: boolean;
25
26
// Event handlers (required for functionality)
27
onStart?: DraggableEventHandler;
28
onDrag?: DraggableEventHandler;
29
onStop?: DraggableEventHandler;
30
onMouseDown?: (e: MouseEvent) => void;
31
32
// Advanced options
33
enableUserSelectHack?: boolean;
34
offsetParent?: HTMLElement;
35
grid?: [number, number];
36
scale?: number;
37
nodeRef?: React.RefObject<HTMLElement>;
38
39
// Required
40
children: React.ReactNode;
41
}
42
```
43
44
**Usage Examples:**
45
46
```javascript
47
import React, { useState } from 'react';
48
import { DraggableCore } from 'react-draggable';
49
50
// Basic DraggableCore with manual positioning
51
function CustomDraggable() {
52
const [position, setPosition] = useState({ x: 0, y: 0 });
53
const [dragging, setDragging] = useState(false);
54
55
const handleStart = (e, data) => {
56
setDragging(true);
57
};
58
59
const handleDrag = (e, data) => {
60
setPosition({
61
x: position.x + data.deltaX,
62
y: position.y + data.deltaY
63
});
64
};
65
66
const handleStop = (e, data) => {
67
setDragging(false);
68
};
69
70
return (
71
<DraggableCore
72
onStart={handleStart}
73
onDrag={handleDrag}
74
onStop={handleStop}
75
>
76
<div
77
style={{
78
transform: `translate(${position.x}px, ${position.y}px)`,
79
backgroundColor: dragging ? '#ff0000' : '#0000ff',
80
padding: '10px',
81
cursor: dragging ? 'grabbing' : 'grab'
82
}}
83
>
84
Custom draggable with manual positioning
85
</div>
86
</DraggableCore>
87
);
88
}
89
```
90
91
### Event Handling
92
93
DraggableCore relies entirely on event handlers for functionality since it maintains no internal state.
94
95
```typescript { .api }
96
/**
97
* Called when drag starts
98
* Must be provided to handle drag initiation
99
* Return false to cancel the drag
100
*/
101
onStart?: DraggableEventHandler;
102
103
/**
104
* Called continuously during drag movement
105
* Must be provided to handle position updates
106
* Return false to stop the current drag
107
*/
108
onDrag?: DraggableEventHandler;
109
110
/**
111
* Called when drag ends
112
* Handle final positioning and cleanup
113
*/
114
onStop?: DraggableEventHandler;
115
116
/**
117
* Called on mouse down events before drag consideration
118
*/
119
onMouseDown?: (e: MouseEvent) => void;
120
121
type DraggableEventHandler = (
122
e: DraggableEvent,
123
data: DraggableData
124
) => void | false;
125
126
interface DraggableData {
127
node: HTMLElement; // The dragged DOM element
128
x: number; // Absolute x position relative to offset parent
129
y: number; // Absolute y position relative to offset parent
130
deltaX: number; // Change in x since last drag event
131
deltaY: number; // Change in y since last drag event
132
lastX: number; // Previous absolute x position
133
lastY: number; // Previous absolute y position
134
}
135
```
136
137
**Usage Examples:**
138
139
```javascript
140
import React, { useState, useRef } from 'react';
141
import { DraggableCore } from 'react-draggable';
142
143
// Complex drag behavior with momentum
144
function MomentumDraggable() {
145
const [position, setPosition] = useState({ x: 0, y: 0 });
146
const [velocity, setVelocity] = useState({ x: 0, y: 0 });
147
const lastTime = useRef(Date.now());
148
149
const handleDrag = (e, data) => {
150
const now = Date.now();
151
const dt = (now - lastTime.current) / 1000;
152
153
// Calculate velocity
154
const vx = data.deltaX / dt;
155
const vy = data.deltaY / dt;
156
setVelocity({ x: vx, y: vy });
157
158
// Update position
159
setPosition({
160
x: position.x + data.deltaX,
161
y: position.y + data.deltaY
162
});
163
164
lastTime.current = now;
165
};
166
167
const handleStop = (e, data) => {
168
// Apply momentum after drag stops
169
const momentumX = velocity.x * 0.1;
170
const momentumY = velocity.y * 0.1;
171
172
setPosition({
173
x: position.x + momentumX,
174
y: position.y + momentumY
175
});
176
};
177
178
return (
179
<DraggableCore onDrag={handleDrag} onStop={handleStop}>
180
<div
181
style={{
182
transform: `translate(${position.x}px, ${position.y}px)`,
183
transition: velocity.x !== 0 ? 'transform 0.3s ease-out' : 'none'
184
}}
185
>
186
Momentum draggable
187
</div>
188
</DraggableCore>
189
);
190
}
191
```
192
193
### Interaction Controls
194
195
Configure drag handles, cancel zones, and interaction behavior (same as Draggable).
196
197
```typescript { .api }
198
/**
199
* CSS selector for elements that can initiate drag
200
*/
201
handle?: string;
202
203
/**
204
* CSS selector for elements that prevent drag initiation
205
*/
206
cancel?: string;
207
208
/**
209
* Completely disable drag functionality
210
*/
211
disabled?: boolean;
212
213
/**
214
* Allow dragging with any mouse button
215
*/
216
allowAnyClick?: boolean;
217
218
/**
219
* Allow normal mobile scrolling behavior
220
*/
221
allowMobileScroll?: boolean;
222
```
223
224
**Usage Examples:**
225
226
```javascript
227
// DraggableCore with handle and cancel
228
function HandleDraggableCore() {
229
const [position, setPosition] = useState({ x: 0, y: 0 });
230
231
const handleDrag = (e, data) => {
232
setPosition({
233
x: position.x + data.deltaX,
234
y: position.y + data.deltaY
235
});
236
};
237
238
return (
239
<DraggableCore
240
handle=".drag-handle"
241
cancel=".no-drag"
242
onDrag={handleDrag}
243
>
244
<div style={{ transform: `translate(${position.x}px, ${position.y}px)` }}>
245
<div className="drag-handle" style={{ cursor: 'grab' }}>
246
✋ Drag Handle
247
</div>
248
<div>
249
<button className="no-drag">Button (not draggable)</button>
250
<p>Content area</p>
251
</div>
252
</div>
253
</DraggableCore>
254
);
255
}
256
```
257
258
### Grid and Scaling
259
260
Apply grid snapping and scaling to drag calculations.
261
262
```typescript { .api }
263
/**
264
* Snap drag movements to grid positions
265
* [x, y] - grid cell size in pixels
266
*/
267
grid?: [number, number];
268
269
/**
270
* Scale factor for drag distance calculations
271
* Useful for zoomed/scaled containers
272
*/
273
scale?: number;
274
```
275
276
**Usage Examples:**
277
278
```javascript
279
// Grid-snapped DraggableCore
280
function GridDraggableCore() {
281
const [position, setPosition] = useState({ x: 0, y: 0 });
282
283
const handleDrag = (e, data) => {
284
// Note: grid snapping is applied to the data values automatically
285
setPosition({
286
x: data.x, // Already snapped to grid
287
y: data.y // Already snapped to grid
288
});
289
};
290
291
return (
292
<DraggableCore grid={[25, 25]} onDrag={handleDrag}>
293
<div
294
style={{
295
transform: `translate(${position.x}px, ${position.y}px)`,
296
width: '50px',
297
height: '50px',
298
backgroundColor: '#4CAF50'
299
}}
300
>
301
Grid Snap
302
</div>
303
</DraggableCore>
304
);
305
}
306
```
307
308
### Advanced Configuration
309
310
Fine-tune behavior for specific use cases and React compatibility.
311
312
```typescript { .api }
313
/**
314
* Add user-select: none during drag to prevent text selection
315
* Default: true
316
*/
317
enableUserSelectHack?: boolean;
318
319
/**
320
* Custom offset parent for position calculations
321
*/
322
offsetParent?: HTMLElement;
323
324
/**
325
* React ref for the draggable element
326
* Recommended for React Strict Mode compatibility
327
*/
328
nodeRef?: React.RefObject<HTMLElement>;
329
```
330
331
**Usage Examples:**
332
333
```javascript
334
import React, { useRef, useState } from 'react';
335
import { DraggableCore } from 'react-draggable';
336
337
// React Strict Mode compatible DraggableCore
338
function StrictModeDraggableCore() {
339
const nodeRef = useRef(null);
340
const [position, setPosition] = useState({ x: 0, y: 0 });
341
342
const handleDrag = (e, data) => {
343
setPosition({
344
x: position.x + data.deltaX,
345
y: position.y + data.deltaY
346
});
347
};
348
349
return (
350
<DraggableCore nodeRef={nodeRef} onDrag={handleDrag}>
351
<div
352
ref={nodeRef}
353
style={{
354
transform: `translate(${position.x}px, ${position.y}px)`
355
}}
356
>
357
Strict Mode Compatible
358
</div>
359
</DraggableCore>
360
);
361
}
362
```
363
364
### Common Use Cases
365
366
DraggableCore is ideal for scenarios requiring custom drag behavior:
367
368
**Custom Positioning Logic:**
369
370
```javascript
371
// Custom boundary checking
372
function BoundedDraggableCore() {
373
const [position, setPosition] = useState({ x: 100, y: 100 });
374
375
const handleDrag = (e, data) => {
376
// Custom boundary logic
377
const newX = Math.max(0, Math.min(400, position.x + data.deltaX));
378
const newY = Math.max(0, Math.min(300, position.y + data.deltaY));
379
380
setPosition({ x: newX, y: newY });
381
};
382
383
return (
384
<DraggableCore onDrag={handleDrag}>
385
<div style={{ transform: `translate(${position.x}px, ${position.y}px)` }}>
386
Custom bounded
387
</div>
388
</DraggableCore>
389
);
390
}
391
```
392
393
**Multi-element Coordination:**
394
395
```javascript
396
// Dragging multiple elements simultaneously
397
function MultiElementDrag() {
398
const [positions, setPositions] = useState([
399
{ x: 0, y: 0 }, { x: 100, y: 0 }, { x: 200, y: 0 }
400
]);
401
402
const handleDrag = (index) => (e, data) => {
403
setPositions(prev => prev.map((pos, i) =>
404
i === index
405
? { x: pos.x + data.deltaX, y: pos.y + data.deltaY }
406
: { x: pos.x + data.deltaX * 0.5, y: pos.y + data.deltaY * 0.5 } // Follow drag
407
));
408
};
409
410
return (
411
<div>
412
{positions.map((pos, index) => (
413
<DraggableCore key={index} onDrag={handleDrag(index)}>
414
<div
415
style={{
416
transform: `translate(${pos.x}px, ${pos.y}px)`,
417
width: '50px',
418
height: '50px',
419
backgroundColor: index === 0 ? '#ff0000' : '#cccccc'
420
}}
421
>
422
{index === 0 ? 'Leader' : 'Follower'}
423
</div>
424
</DraggableCore>
425
))}
426
</div>
427
);
428
}
429
```