npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-drag-drop-board

v1.0.7

Published

A lightweight, reusable React Native drag-and-drop matching board powered by Gesture Handler and Reanimated.

Readme

react-native-drag-drop-board

A reusable, TypeScript-first React Native drag-and-drop matching board for educational activities, games, onboarding flows, quizzes, and custom matching interactions.


Demo

Note: GitHub and npm's README renderers strip <video> tags for security reasons, so the demo won't play inline there — viewers will just see the link. If you want it to play inline on npm/GitHub, convert the clip to a .gif and reference it with a normal <img> tag instead.


Features

  • Two-column drop-zone and draggable-tile layout
  • Smooth spring-based drag, snap, and bounce animations
  • Configurable snap distance
  • Match state support through matchedIds
  • Reset support through resetKey
  • Fully custom React content for zones and draggable tiles
  • Custom draggable tile colors
  • TypeScript types included
  • Built with react-native-gesture-handler
  • Built with react-native-reanimated
  • Application-specific state remains outside the component
  • Suitable for games, educational activities, quizzes, onboarding, and matching experiences

Installation

Install the package together with its peer dependencies:

npm install react-native-drag-drop-board react-native-gesture-handler react-native-reanimated

Or:

yarn add react-native-drag-drop-board react-native-gesture-handler react-native-reanimated

Peer dependencies

This package requires:

  • react-native-gesture-handler
  • react-native-reanimated

Follow the official setup instructions for those libraries for the React Native version used by your application.


Quick Start

import React, { useCallback, useState } from 'react';
import { Text, View } from 'react-native';
import DragDropBoard, {
  DragDropItem,
} from 'react-native-drag-drop-board';

const ITEMS: DragDropItem[] = [
  {
    id: 'a',
    color: '#FF6B6B',
    zoneContent: <Text>A</Text>,
    dragContent: (
      <Text
        style={{
          color: '#fff',
          fontWeight: '800',
          fontSize: 28,
        }}
      >
        A
      </Text>
    ),
  },
  {
    id: 'b',
    color: '#4ECDC4',
    zoneContent: <Text>B</Text>,
    dragContent: (
      <Text
        style={{
          color: '#fff',
          fontWeight: '800',
          fontSize: 28,
        }}
      >
        B
      </Text>
    ),
  },
];

export default function Example() {
  const [matchedIds, setMatchedIds] = useState<Set<string>>(new Set());
  const [resetKey, setResetKey] = useState(0);

  const handleMatch = useCallback((id: string) => {
    setMatchedIds((current) => new Set(current).add(id));
  }, []);

  return (
    <View style={{ flex: 1 }}>
      <DragDropBoard
        items={ITEMS}
        matchedIds={matchedIds}
        onMatch={handleMatch}
        resetKey={resetKey}
        snapDistance={100}
      />
    </View>
  );
}

How It Works

Each item represents one matching pair.

The zoneContent is rendered in the left drop zone, while dragContent is rendered as the draggable tile on the right.

When a tile is released close enough to its matching zone:

  1. The board calculates the distance between the tile and its matching zone.
  2. If the distance is within snapDistance, the tile springs into the zone.
  3. onMatch(id) is called.
  4. The application can add the ID to matchedIds.
  5. The matched tile becomes disabled and visually faded.

If the tile is released outside the configured snap distance, it springs back to its original position.


API

DragDropBoardProps

| Prop | Type | Default | Description | |---|---|---:|---| | items | DragDropItem[] | Required | Matching pairs and their rendered content. | | onMatch | (id: string) => void | Required | Called when a tile successfully snaps into its matching zone. | | matchedIds | ReadonlySet<string> | Empty set | IDs that are already matched. Matched tiles are disabled and faded. | | resetKey | number \| string | 0 | Change this value to return all draggable tiles to their original positions. | | snapDistance | number | 100 | Maximum distance in pixels for a successful match. | | snapSpring | { damping: number; stiffness: number } | { damping: 14, stiffness: 140 } | Spring configuration used when a tile successfully snaps. | | bounceSpring | { damping: number; stiffness: number } | { damping: 9, stiffness: 160 } | Spring configuration used when a tile misses its matching zone. | | style | StyleProp<ViewStyle> | — | Style applied to the root gesture-handler container. |


DragDropItem

export interface DragDropItem {
  id: string;
  zoneContent: React.ReactNode;
  dragContent: React.ReactNode;
  color?: string;
}

Properties

id

Unique identifier for the matching pair.

id: 'apple'

The same ID is used to associate the draggable tile with its drop zone.

zoneContent

React content displayed inside the matching drop zone.

zoneContent={<Text>🍎</Text>}

dragContent

React content displayed inside the draggable tile.

dragContent={<Text>Apple</Text>}

color

Optional background color for the draggable tile.

color: '#FF6B6B'

Matching State

The board does not own your application matching state.

Your screen controls matchedIds:

const [matchedIds, setMatchedIds] = useState<Set<string>>(new Set());

const handleMatch = useCallback((id: string) => {
  setMatchedIds((current) => {
    const next = new Set(current);
    next.add(id);
    return next;
  });
}, []);

Then:

<DragDropBoard
  items={ITEMS}
  matchedIds={matchedIds}
  onMatch={handleMatch}
/>

This makes it easy to connect the board to:

  • Redux
  • Zustand
  • Context
  • local React state
  • game progress
  • analytics
  • sound effects
  • scoring systems

Reset the Board

Use resetKey when you want all draggable tiles to return to their original positions.

const [resetKey, setResetKey] = useState(0);

const resetGame = () => {
  setMatchedIds(new Set());
  setResetKey((current) => current + 1);
};

Then:

<DragDropBoard
  items={ITEMS}
  matchedIds={matchedIds}
  onMatch={handleMatch}
  resetKey={resetKey}
/>

Every time resetKey changes, the draggable tiles reset.


Custom Content

The board accepts any React content.

Images

{
  id: 'cat',
  color: '#FF6B6B',
  zoneContent: (
    <Image
      source={require('./assets/cat.png')}
      style={{ width: 70, height: 70 }}
    />
  ),
  dragContent: (
    <Text style={{ color: '#fff', fontSize: 24 }}>
      CAT
    </Text>
  ),
}

Icons

{
  id: 'home',
  zoneContent: <HomeIcon size={48} />,
  dragContent: <Text>HOME</Text>,
}

Custom components

{
  id: 'one',
  zoneContent: <MyDropZone />,
  dragContent: <MyDraggableTile />,
}

Animation Configuration

Snap animation

<DragDropBoard
  items={ITEMS}
  onMatch={handleMatch}
  snapSpring={{
    damping: 14,
    stiffness: 140,
  }}
/>

Failed-drop animation

<DragDropBoard
  items={ITEMS}
  onMatch={handleMatch}
  bounceSpring={{
    damping: 9,
    stiffness: 160,
  }}
/>

Snap distance

<DragDropBoard
  items={ITEMS}
  onMatch={handleMatch}
  snapDistance={120}
/>

A larger value makes matching more forgiving.


Architecture

The package intentionally keeps application-specific data out of the component.

Application owns

  • Matching state
  • Reset state
  • Game progress
  • Scores
  • Sounds
  • Analytics
  • Content
  • Navigation
  • Business rules

Board owns

  • Gesture handling
  • Drag movement
  • Layout measurement
  • Drop-distance calculation
  • Snap animation
  • Bounce-back animation
  • Rendering of zones and draggable tiles

This keeps the component reusable across different applications and use cases.


Example Use Cases

Educational apps

  • Match letters with pictures
  • Match numbers with quantities
  • Match animals with sounds
  • Match words with images
  • Match shapes with names

Games

  • Memory matching
  • Puzzle interactions
  • Sorting games
  • Learning games
  • Drag-and-drop challenges

Product / onboarding experiences

  • Feature matching
  • Preference selection
  • Interactive tutorials
  • Guided onboarding
  • Categorization experiences

Troubleshooting

Dragging does not work

Make sure your application is correctly configured for react-native-gesture-handler.

For applications where the gesture-handler root is required, make sure the relevant screen or application root is wrapped appropriately according to your Gesture Handler version.

Reanimated errors

Make sure react-native-reanimated is correctly installed and configured for your React Native version.

After changing native dependencies, rebuild the application.

For iOS:

cd ios
pod install
cd ..

Then rebuild the app.

Demo video is not showing on npm or GitHub

Both renderers strip raw <video> tags for security reasons, so:

  1. Convert DEMO.mov to a .gif (e.g. with ffmpeg or a tool like Gifski).
  2. Reference the .gif with a plain <img> tag instead of <video>.
  3. Keep the file under a few MB — large GIFs load slowly or get truncated by npm's README size limit.

License

MIT © shar25111996