Mohammad Shehadeh

Sortable

Component

About

Drag-to-reorder list on dnd-kit: pointer and keyboard sorting, handle or whole-item dragging, and controls inside items that stay clickable.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Sortable to your project and fits it to your theme.

Code

How Sortable is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

'use client';

import { Sortable, SortableHandle, SortableItem } from '@/components/ui/sortable';
import * as React from 'react';

const tags = ['react', 'tailwind', 'radix', 'cmdk', 'lucide'];

export default function SortableDemo() {
  const tasks = {
    'design-review': 'Design review with the product team',
    'fix-onboarding': 'Fix onboarding empty state',
    'ship-registry': 'Ship the registry build pipeline',
    'write-changelog': 'Write the release changelog',
  };

  const [taskOrder, setTaskOrder] = React.useState(Object.keys(tasks));
  const [tagOrder, setTagOrder] = React.useState(tags);

  return (
    <div className="grid w-full max-w-2xl gap-8">
      <div className="grid gap-4">
        <p className="text-xs text-muted-foreground uppercase">Tasks</p>
        <Sortable value={taskOrder} onValueChange={setTaskOrder}>
          {Object.entries(tasks).map(([id, title]) => (
            <SortableItem key={id} value={id}>
              <SortableHandle />
              <span className="flex-1 truncate">{title}</span>
              <span className="font-mono text-[10px] text-muted-foreground">{id}</span>
            </SortableItem>
          ))}
        </Sortable>
        <p className="font-mono text-[10px] text-muted-foreground">order: [{taskOrder.join(', ')}]</p>
      </div>

      <div className="grid gap-4">
        <p className="text-xs text-muted-foreground uppercase">Tags</p>
        <Sortable orientation="horizontal" value={tagOrder} onValueChange={setTagOrder}>
          {tags.map((tag) => (
            <SortableItem key={tag} value={tag}>
              {tag}
            </SortableItem>
          ))}
        </Sortable>
        <p className="font-mono text-[10px] text-muted-foreground">order: [{tagOrder.join(', ')}]</p>
      </div>

      <div className="grid gap-4">
        <p className="text-xs text-muted-foreground uppercase">Disabled item</p>
        <Sortable defaultValue={['draft', 'locked', 'review', 'published']} className="max-w-sm">
          <SortableItem value="draft">
            <SortableHandle />
            Draft
          </SortableItem>
          <SortableItem value="locked" disabled>
            <SortableHandle />
            Locked (disabled)
          </SortableItem>
          <SortableItem value="review">
            <SortableHandle />
            In review
          </SortableItem>
          <SortableItem value="published">
            <SortableHandle />
            Published
          </SortableItem>
        </Sortable>
      </div>
    </div>
  );
}

Dependencies

More by Mohammad Shehadeh

View all
  • Feature Problem Cards

    Feature Problem Cards

    84
  • Feature Grid Spotlight Cards

    Feature Grid Spotlight Cards

    70
  • Dithered Shader CTA

    Dithered Shader CTA

    63
  • Split Login

    Split Login

    60
  • Ecommerce Category Page

    Ecommerce Category Page

    59
  • FAQ Two Column

    FAQ Two Column

    59
  • Comparison Table

    Comparison Table

    58
  • Comparison Section

    Comparison Section

    57

Explore more

Part of Hirael — browse the full library on 21st.dev.