Back to Dnd Kit

useSortable

apps/docs/solid/hooks/use-sortable.mdx

latest7.0 KB
Original Source

import {Story} from '/snippets/story.mdx'; import {CodeSandbox} from '/snippets/sandbox.mdx'; import {sortableStyles} from '/snippets/code.mdx';

<Story id="sortable-vertical-list--basic-setup" framework="solid" height="320" hero />

Usage

The useSortable hook combines draggable and droppable behavior with sorting logic. Import it from @dnd-kit/solid/sortable.

<CodeSandbox template="solid" files={{ 'App.tsx': {code: appCode, active: true}, 'styles.css': {code: sortableStyles, hidden: true}, }} height={560} previewHeight={180} />

<Note> Use **getter syntax** for reactive props to maintain Solid's fine-grained reactivity. Without getters, the hook reads prop values once during setup and never re-runs its effects when they change. </Note>

Without the move helper

The example above uses the move helper from @dnd-kit/helpers, a convenience function that takes your items and a drag event and returns a new array with the item moved to its new position. It supports flat arrays and grouped records, handles canceled drags, and works with optimistic sorting out of the box.

If you need more control over state updates, you can manage state manually using the isSortable type guard and the sortable properties (initialIndex, index).

With optimistic sorting enabled (the default), you only need to handle the onDragEnd event:

tsx
import {createSignal, For} from 'solid-js';
import {DragDropProvider} from '@dnd-kit/solid';
import {useSortable, isSortable} from '@dnd-kit/solid/sortable';

function SortableItem(props) {
  const {ref} = useSortable({
    get id() { return props.id; },
    get index() { return props.index; },
  });

  return <li ref={ref} class="item">Item {props.id}</li>;
}

export default function App() {
  const [items, setItems] = createSignal([1, 2, 3, 4]);

  return (
    <DragDropProvider
      onDragEnd={(event) => {
        if (event.canceled) return;

        const {source} = event.operation;

        if (isSortable(source)) {
          const {initialIndex, index} = source;

          if (initialIndex !== index) {
            setItems((items) => {
              const newItems = [...items];
              const [removed] = newItems.splice(initialIndex, 1);
              newItems.splice(index, 0, removed);
              return newItems;
            });
          }
        }
      }}
    >
      <ul class="list">
        <For each={items()}>
          {(id, index) => <SortableItem id={id} index={index()} />}
        </For>
      </ul>
    </DragDropProvider>
  );
}
<Tip> You can call `event.preventDefault()` in an `onDragOver` handler to prevent the `OptimisticSortingPlugin` from optimistically updating for that specific event. This is useful when you want to conditionally block certain moves. </Tip> <Info> Learn more about [optimistic sorting, type guards, and manual state management](/concepts/sortable#optimistic-sorting) in the Sortable concepts page. </Info>

Input

<ParamField path="id" type="UniqueIdentifier" required> A unique identifier. Use `get id() { return props.id }` for reactive values. </ParamField> <ParamField path="index" type="number" required> The current index in the sorted list. Use `get index() { return props.index }` for reactive values. </ParamField> <ParamField path="group" type="string" optional> The group this sortable belongs to. Used for sorting across multiple lists. </ParamField> <ParamField path="handle" type="Element" optional> A handle element. Use the `handleRef` callback to set it. </ParamField> <ParamField path="accept" type="string | string[]" optional> The types of draggable elements this sortable accepts. </ParamField> <ParamField path="type" type="string" optional> The type of this sortable element. </ParamField> <ParamField path="plugins" type="PluginDescriptor[]" optional> An array of plugin descriptors for per-entity plugin configuration. Use `Plugin.configure()` to create descriptors. For example, `Feedback.configure({ feedback: 'clone' })`. </ParamField> <ParamField path="transition" type="SortableTransition" optional> Animation transition configuration. </ParamField> <ParamField path="modifiers" type="Modifier[]" optional> Modifiers to apply to this sortable instance. </ParamField> <ParamField path="sensors" type="Sensor[]" optional> Sensors to use for this sortable instance. </ParamField> <ParamField path="collisionDetector" type="CollisionDetector" optional> A custom collision detection algorithm. </ParamField> <ParamField path="collisionPriority" type="number" optional> The collision priority of this sortable element. Higher values take precedence when multiple droppable elements overlap. </ParamField> <ParamField path="disabled" type="boolean" optional> Whether the sortable is disabled. </ParamField> <ParamField path="data" type="Data" optional> Custom data to attach to this sortable instance. </ParamField>

Output

<ResponseField name="ref" type="(element: Element) => void"> A callback ref to attach to the sortable element. </ResponseField> <ResponseField name="handleRef" type="(element: Element) => void"> A callback ref for a drag handle. </ResponseField> <ResponseField name="sourceRef" type="(element: Element) => void"> A callback ref for the drag source element. </ResponseField> <ResponseField name="targetRef" type="(element: Element) => void"> A callback ref for the drop target element. </ResponseField> <ResponseField name="isDragging" type="() => boolean" note="accessor"> Whether this element is currently being dragged. Call as `isDragging()` in JSX. </ResponseField> <ResponseField name="isDropping" type="() => boolean" note="accessor"> Whether this element is in the process of being dropped. Call as `isDropping()` in JSX. </ResponseField> <ResponseField name="isDragSource" type="() => boolean" note="accessor"> Whether this element is the source of the current drag operation. Call as `isDragSource()` in JSX. </ResponseField> <ResponseField name="isDropTarget" type="() => boolean" note="accessor"> Whether this element is currently a drop target. Call as `isDropTarget()` in JSX. </ResponseField> <ResponseField name="sortable" type="Sortable"> The underlying `Sortable` instance. </ResponseField>

export const appCode = ` import {createSignal, For} from 'solid-js'; import {DragDropProvider} from '@dnd-kit/solid'; import {useSortable} from '@dnd-kit/solid/sortable'; import {move} from '@dnd-kit/helpers'; import './styles.css';

function SortableItem(props) { const {ref} = useSortable({ get id() { return props.id; }, get index() { return props.index; }, });

return <li ref={ref} class="item">Item {props.id}</li>; }

export default function App() { const [items, setItems] = createSignal([1, 2, 3, 4]);

return ( <DragDropProvider onDragEnd={(event) => { setItems((items) => move(items, event)); }} > <ul class="list"> <For each={items()}> {(id, index) => <SortableItem id={id} index={index()} />} </For> </ul> </DragDropProvider> ); } `.trim();