# How to Implement Drag and Drop in React Native


### Key Takeaways

*   PanResponder is React Native’s API for handling continuous touch gestures — drags, swipes, multi-touch. It’s the right tool for drag and drop.
    
*   Four callbacks matter most: onStartShouldSetPanResponder (claim the gesture), onPanResponderGrant (gesture confirmed, initialize), onPanResponderMove (finger is moving), onPanResponderRelease (finger lifted).
    
*   Animated.ValueXY tracks position. Combine it with pan.getLayout() in the style to move the element as the gesture updates.
    
*   setOffset and flattenOffset handle the position accumulation problem — without them, the element jumps back to 0,0 on every new drag.
    
*   react-native-draggable-flatlist is worth knowing for sortable lists specifically. PanResponder covers general drag-and-drop; that library handles the common case of draggable list items with reordering.
    

### Understanding PanResponder in React Native

PanResponder is a gesture API that reconciles multiple touch events into a single responder. It’s how React Native handles the situation where multiple components might want to respond to the same touch — the responder system decides which one “owns” the gesture.

For drag and drop, you care about four callbacks:

**onStartShouldSetPanResponder** — called when a touch starts. Return true to claim the gesture for this component.

**onPanResponderGrant** — called once after the component claims the gesture. This is where you initialize position tracking before the drag begins.

**onPanResponderMove** — called continuously as the finger moves. This is where position updates happen.

**onPanResponderRelease** — called when the finger lifts. This is where you finalize position or handle drop logic.

There are other callbacks (onMoveShouldSetPanResponder, onPanResponderTerminate, etc.) that handle edge cases like interrupted gestures. For a basic implementation, the four above are sufficient.

### Implementing Drag and Drop in React Native: A Practical Example

The implementation below creates a draggable box that follows your finger anywhere on the screen.

### Step 1: Import Dependencies

import React, { useState } from ‘react’;

import { View, Animated, PanResponder, StyleSheet } from ‘react-native’;

Animated and PanResponder are both from React Native core — no additional packages needed.

### Step 2: Create the Draggable Component

```plaintext
const DraggableComponent = () => {
 
  const [pan, setPan] = useState(new Animated.ValueXY());
 
  const panResponder = PanResponder.create({
 
    onStartShouldSetPanResponder: () => true,
 
    onPanResponderGrant: () => {
 
      pan.setOffset({
 
        x: pan.x._value,
 
        y: pan.y._value,
 
      });
 
      pan.setValue({ x: 0, y: 0 });
 
    },
 
    onPanResponderMove: Animated.event(
 
      [null, { dx: pan.x, dy: pan.y }],
 
      { useNativeDriver: false }
 
    ),
 
    onPanResponderRelease: () => {
 
      pan.flattenOffset();
 
    },
 
  });
 
  return (
 
    <Animated.View
 
      style={[pan.getLayout(), styles.draggable]}
 
      {…panResponder.panHandlers}
 
    >
 
      {/* Render your draggable content here */}
 
    </Animated.View>
 
  );
 
};
```

The setOffset / flattenOffset pattern in onPanResponderGrant and onPanResponderRelease is the part most implementations get wrong first. Without it, every new drag resets dx and dy to 0, making the element jump back to wherever it started. The offset stores where the element currently sits, so the next drag adds to that position correctly.

Animated.event in onPanResponderMove is the clean way to connect gesture deltas directly to an animated value. The \[null, { dx: pan.x, dy: pan.y }\] maps the second argument of the move callback (the gesture state) onto pan.x and pan.y. useNativeDriver: false is required here because layout animations can’t run on the native thread.

### Step 3: Use the Draggable Component

const App = () => {

return (

```plaintext
<View style={styles.container}>

  {/* Other components */}

  <DraggableComponent />

  {/* Other components */}

</View>
```

### Step 4: Style Your Components

```plaintext
const styles = {

  container: {

    flex: 1,

    justifyContent: ‘center’,

    alignItems: ‘center’,

  },

  draggable: {

    width: 100,

    height: 100,

    backgroundColor: ‘blue’,

  },

};
```

StyleSheet.create instead of a plain object — not required, but it validates styles at creation time and gives better performance through reference caching.

### A Few Things Worth Knowing

**The offset problem.** The setOffset / flattenOffset pattern is necessary because Animated.event tracks deltas from the start of each gesture, not absolute position. When a gesture ends, the delta resets to 0. Without the offset, the next drag starts as if the element is at its original position. With the offset, you store the accumulated position and add new deltas to it correctly.

**useNativeDriver: false is unavoidable for layout.** pan.getLayout() uses top and left CSS properties for positioning. The native driver doesn’t support top/left animation — only transform. If you want to use the native driver (better performance), change pan.getLayout() to { transform: pan.getTranslateTransform() }. The visual result is identical, but native driver animation is smoother on lower-end devices.

**Boundaries and constraints.** Nothing in the example prevents the element from being dragged off-screen. If you need bounded dragging, clamp pan.x and pan.y in onPanResponderMove using Animated.diffClamp or by manually checking against screen dimensions from Dimensions.get(‘window’).

**Sortable lists.** If what you actually need is a draggable list where items reorder on drop, PanResponder is the wrong starting point. react-native-draggable-flatlist handles the complex hit-testing and reordering logic that would otherwise be a significant custom implementation. Use PanResponder for free-form drag and drop; use a list library for sortable list items.

### Conclusion

PanResponder gives you the gesture primitives. Animated.ValueXY gives you the position tracking. Combined with the setOffset / flattenOffset pattern, you get a draggable component that handles accumulated position correctly across multiple drag operations.

The pattern above handles the standard case. From here you can extend it: snap-to-grid behavior (round x and y on release), drop zones (check position in onPanResponderRelease against known bounds), drag handles (attach the PanResponder to a specific handle view rather than the whole component).

## **About Innostax**

[**Innostax**](https://innostax.com/) specializes in managed engineering teams and was founded in 2014, and is headquartered in Framingham, Massachusetts. We establish engineering teams with accountability as a priority for both startups and enterprises, helping them achieve consistent software velocity with no customer churn.

Read more :  **How to Implement Drag and Drop in React Native**
