Complete Guide to dnd-kit Drag and Drop in Chrome Extensions

16 min read

Complete Guide to dnd-kit Drag and Drop in Chrome Extensions

Complete Guide to dnd-kit Drag and Drop in Chrome Extensions

Adding drag and drop functionality to your Chrome extension can dramatically improve user experience, making your interface more intuitive and interactive. Whether you are building a tab manager, a bookmark organizer, a task management tool, or any extension that involves organizing items, implementing smooth drag and drop functionality is essential. This is where dnd-kit comes in — a modern, lightweight, and accessible drag and drop library that works seamlessly with Chrome extensions.

In this comprehensive guide, we will explore how to integrate dnd-kit into your Chrome extension, covering everything from basic setup to advanced sorting algorithms and accessibility considerations.


What is dnd-kit and Why It Matters for Chrome Extensions

dnd-kit is a modern drag and drop library for React that provides a lightweight, accessible, and performant way to add drag functionality to your applications. Unlike older drag and drop libraries like react-beautiful-dnd or react-dnd, dnd-kit is designed with flexibility and future-proofing in mind, making it an excellent choice for Chrome extensions built with modern React patterns.

When building Chrome extensions, you often need to create interfaces where users can reorder items, move elements between containers, or rearrange their workspace. A dnd kit extension can solve these problems elegantly, providing the building blocks for:

  • Sortable lists: Reorder items in a single list or multiple lists
  • Draggable elements: Move items between different containers or sections
  • Grid layouts: Implement draggable items in responsive grid systems
  • Multi-select drag: Allow users to drag multiple items simultaneously

The library is modular, meaning you only import what you need, keeping your extension’s bundle size minimal — a critical consideration for Chrome extensions where performance directly impacts user experience.


Key Features of dnd-kit for Extension Development

dnd-kit offers several features that make it particularly well-suited for Chrome extension development:

1. Lightweight Bundle Size

Chrome extensions face strict performance constraints. dnd-kit is designed to be lightweight, with a minimal footprint that won’t significantly impact your extension’s loading time. The core library is just a few kilobytes, and you can further reduce size by importing only the sensors and modifiers you need.

2. Accessibility First

Accessibility is crucial for Chrome extensions that aim to reach a broad user base. dnd-kit provides full keyboard support out of the box, allowing users to navigate and drag items using keyboard shortcuts. Screen readers work seamlessly with the library’s ARIA attributes, making your extension usable by people with disabilities.

3. Framework Agnostic Core

While dnd-kit is built for React, its core functionality is framework-agnostic. This means the underlying algorithms and patterns can potentially be adapted for other frameworks if your extension needs to use something other than React.

4. Powerful Sensors

dnd-kit supports multiple input methods through its sensor system:

  • Pointer sensors: Handle mouse and touch input
  • Keyboard sensors: Enable full keyboard navigation
  • Touch sensors: Optimize for mobile and tablet experiences

5. Customizable Collision Detection

The library includes multiple collision detection algorithms, allowing you to choose the approach that best fits your use case:

  • Rectangle intersection: Default collision detection
  • Closest center: Find the closest droppable center
  • Pointer within: Check if the pointer is within a droppable

Setting Up dnd-kit in Your Chrome Extension

Let’s walk through the process of adding dnd-kit to a Chrome extension project. This section assumes you have a basic Chrome extension structure with React already set up.

Installation

First, install dnd-kit and its dependencies using your preferred package manager:

npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities

Or if you are using Yarn:

yarn add @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities

Basic Implementation

Here is a simple example of implementing a sortable list in your Chrome extension using dnd-kit:

import React, { useState } from 'react';
import {
  DndContext,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
} from '@dnd-kit/core';
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  verticalListSortingStrategy,
  useSortable,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';

function SortableItem({ id, content }) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
  } = useSortable({ id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    padding: '12px',
    margin: '8px 0',
    backgroundColor: '#fff',
    border: '1px solid #ddd',
    borderRadius: '4px',
    cursor: 'grab',
  };

  return (
    <div ref={setNodeRef} style={style} {...attributes} {...listeners}>
      {content}
    </div>
  );
}

function SortableList() {
  const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3', 'Item 4']);

  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    })
  );

  function handleDragEnd(event) {
    const { active, over } = event;

    if (active.id !== over.id) {
      setItems((items) => {
        const oldIndex = items.indexOf(active.id);
        const newIndex = items.indexOf(over.id);
        return arrayMove(items, oldIndex, newIndex);
      });
    }
  }

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragEnd={handleDragEnd}
    >
      <SortableContext items={items} strategy={verticalListSortingStrategy}>
        {items.map((id) => (
          <SortableItem key={id} id={id} content={id} />
        ))}
      </SortableContext>
    </DndContext>
  );
}

export default SortableList;

This basic implementation provides a fully functional sortable list with mouse and keyboard support. The extension user can click and drag items to reorder them, or use arrow keys when an item is focused to reorder via keyboard.


Advanced dnd-kit Patterns for Chrome Extensions

Once you have the basics working, you can implement more advanced patterns to create sophisticated drag and drop experiences in your Chrome extension.

Multiple Sortable Lists

Many Chrome extensions need to manage items across multiple lists or columns. For example, a Kanban-style task manager might have columns for “To Do,” “In Progress,” and “Done.” dnd-kit handles this scenario elegantly with its multiple containers support.

import React, { useState } from 'react';
import {
  DndContext,
  closestCorners,
  DragOverlay,
  useSensor,
  useSensors,
  PointerSensor,
} from '@dnd-kit/core';

const COLUMN_IDS = ['todo', 'in-progress', 'done'];

function MultiColumnKanban() {
  const [items, setItems] = useState({
    todo: ['Task 1', 'Task 2', 'Task 3'],
    'in-progress': ['Task 4', 'Task 5'],
    done: ['Task 6'],
  });

  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: {
        distance: 8,
      },
    })
  );

  const findContainer = (id) => {
    if (id in items) {
      return id;
    }
    return Object.keys(items).find((key) => items[key].includes(id));
  };

  function handleDragOver(event) {
    const { active, over } = event;
    const overId = over?.id;

    if (!overId || active.id === overId) {
      return;
    }

    const activeContainer = findContainer(active.id);
    const overContainer = findContainer(overId) || overId;

    if (!activeContainer || !overContainer || activeContainer === overContainer) {
      return;
    }

    setItems((prev) => {
      const activeItems = prev[activeContainer];
      const overItems = prev[overContainer];
      const activeIndex = activeItems.indexOf(active.id);
      const overIndex = overItems.indexOf(overId);

      let newIndex;
      if (overId in prev) {
        newIndex = overItems.length + 1;
      } else {
        const isBelowOverItem =
          over &&
          active.rect.current.translated &&
          active.rect.current.translated.top > over.rect.top + over.rect.height;

        const modifier = isBelowOverItem ? 1 : 0;
        newIndex = overIndex >= 0 ? overIndex + modifier : overItems.length + 1;
      }

      return {
        ...prev,
        [activeContainer]: [
          ...prev[activeContainer].filter((item) => item !== active.id),
        ],
        [overContainer]: [
          ...prev[overContainer].slice(0, newIndex),
          active.id,
          ...prev[overContainer].slice(newIndex, prev[overContainer].length),
        ],
      };
    });
  }

  function handleDragEnd(event) {
    const { active, over } = event;
    const activeContainer = findContainer(active.id);
    const overContainer = findContainer(over?.id);

    if (
      activeContainer &&
      overContainer &&
      activeContainer === overContainer
    ) {
      const activeIndex = items[activeContainer].indexOf(active.id);
      const overIndex = items[overContainer].indexOf(over.id);

      if (activeIndex !== overIndex) {
        setItems((prev) => ({
          ...prev,
          [activeContainer]: arrayMove(
            prev[activeContainer],
            activeIndex,
            overIndex
          ),
        }));
      }
    }
  }

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCorners}
      onDragOver={handleDragOver}
      onDragEnd={handleDragEnd}
    >
      {/* Render your columns here */}
    </DndContext>
  );
}

Drag Between Lists

A drag drop library chrome extension often needs to allow users to move items between different lists or containers. This is a common pattern for bookmark managers, tab group organizers, and task management tools.

The key to implementing drag between lists is handling the onDragOver event to provide immediate visual feedback as the user drags an item over a different container.

Implementing Drag Handles

Sometimes you want the drag functionality to be triggered only from a specific handle element within your item, rather than the entire item. This is useful when your items contain interactive elements like checkboxes, buttons, or text inputs.

function SortableItemWithHandle({ id, content }) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.5 : 1,
  };

  return (
    <div ref={setNodeRef} style={style}>
      <button className="drag-handle" {...attributes} {...listeners}></button>
      <span className="content">{content}</span>
    </div>
  );
}

Best Practices for dnd-kit in Chrome Extensions

When implementing dnd-kit in your Chrome extension, following these best practices will ensure a smooth user experience and maintain performance.

1. Optimize for Chrome’s Performance Model

Chrome extensions run in a multi-process environment. Keep these performance tips in mind:

  • Use virtualization for large lists: If your extension displays hundreds or thousands of items, consider using a virtualization library like react-window in combination with dnd-kit to render only visible items.
  • Minimize re-renders: Use React.memo and useMemo for your sortable items to prevent unnecessary re-renders during drag operations.
  • Lazy load dnd-kit features: Import only the dnd-kit modules you need rather than importing the entire library.

2. Handle Edge Cases Gracefully

Chrome extensions often run in complex environments with multiple tabs and windows. Handle these edge cases:

  • Cross-window drag: Be aware that dnd-kit’s default behavior may not work across different browser windows. Design your extension accordingly.
  • Rapid interactions: Implement proper state management to handle rapid drag operations without breaking the UI.
  • Empty states: Provide visual feedback when containers are empty and can accept dropped items.

3. Maintain State Persistence

Users expect their arrangement of items to persist across browser sessions. Implement proper state persistence:

  • Use Chrome’s storage API (chrome.storage.local or chrome.storage.sync) to save item order after each drag operation.
  • Handle migration scenarios when your data structure changes between extension versions.

4. Test Across Different Scenarios

Chrome extensions run in various contexts:

  • Popup windows: Limited space, typically short interactions
  • Options pages: Full-page interfaces with more complex layouts
  • Content scripts: Injecting drag and drop into web pages (requires careful testing)
  • DevTools panels: Specific constraints within the DevTools environment

Test your dnd-kit implementation in each context to ensure it works correctly.


Accessibility Considerations

Accessibility is not optional — it is essential for reaching all users and is often a requirement for certain markets and use cases. dnd-kit provides excellent accessibility support, but you need to configure it properly.

Keyboard Navigation

dnd-kit’s keyboard sensor provides comprehensive keyboard support:

  • Arrow keys: Navigate between sortable items
  • Space: Pick up and drop items
  • Escape: Cancel the current drag operation
  • Arrow keys (while dragging): Move the item within the list

Screen Reader Support

The library automatically adds appropriate ARIA attributes to sortable elements. However, you should also provide meaningful labels and descriptions for your specific use case:

<div
  aria-label={`Task: ${taskName}, position ${index + 1} of ${totalItems}`}
  role="listitem"
  {...attributes}
  {...listeners}
>
  {taskName}
</div>

Focus Management

Ensure that focus is properly managed during drag operations. When an item is picked up or dropped, focus should move to the appropriate element to maintain keyboard navigation flow.


Common Issues and Solutions

Here are solutions to common problems you might encounter when implementing dnd-kit in Chrome extensions:

Issue: Drag Not Working in Popup

Chrome popup windows have limited functionality. If drag and drop does not work in your popup, ensure that you have properly configured the pointer sensor with activation constraints:

const sensors = useSensors(
  useSensor(PointerSensor, {
    activationConstraint: {
      distance: 5, // Require 5px movement before drag starts
    },
  })
);

Issue: Items Not Dropping Correctly

If items are not dropping in the expected position, adjust your collision detection algorithm:

<DndContext
  collisionDetection={closestCorners} // Try closestCenter, closestCorners, or rectIntersection
  // ...
>

Issue: Performance Issues with Many Items

For extensions with many sortable items, implement virtualization or consider using a different UI pattern that does not require rendering all items simultaneously.


Real-World Examples of dnd-kit in Chrome Extensions

Many successful Chrome extensions use drag and drop functionality that could benefit from dnd-kit implementation:

  • Tab managers: Extensions like Tab Tree, Tree Style Tab, and Workona use drag and drop to reorganize tabs and groups
  • Bookmark managers: Allow users to drag bookmarks into folders or reorder them
  • Task managers: Kanban-style task boards with draggable cards between columns
  • Note-taking apps: Extensions like Google Keep clone implementations use drag and drop for organizing notes
  • Email clients: Gmail and similar extensions use drag and drop for organizing emails into labels

Conclusion

dnd-kit is an excellent choice for implementing drag and drop functionality in Chrome extensions. Its lightweight footprint, accessibility support, and flexible API make it ideal for the unique constraints of browser extension development. Whether you are building a simple sortable list or a complex multi-column Kanban board, dnd-kit provides the tools you need to create intuitive, performant drag and drop experiences.

The key to success is starting with the basic implementation and progressively adding complexity as needed. Remember to optimize for Chrome’s performance model, handle edge cases gracefully, maintain state persistence, and ensure accessibility for all users.

For more information on building Chrome extensions with modern React patterns, explore the full Chrome Extension Guide — your comprehensive resource for everything from getting started to publishing on the Chrome Web Store.


This guide is part of the Chrome Extension Guide by theluckystrike — your comprehensive resource for Chrome extension development.

No previous article
No next article