{"id":13475625,"url":"https://github.com/SortableJS/react-sortablejs","last_synced_at":"2025-03-27T00:31:38.234Z","repository":{"id":37493097,"uuid":"53563895","full_name":"SortableJS/react-sortablejs","owner":"SortableJS","description":"React bindings for SortableJS","archived":false,"fork":false,"pushed_at":"2023-12-06T13:47:48.000Z","size":6914,"stargazers_count":2049,"open_issues_count":105,"forks_count":209,"subscribers_count":26,"default_branch":"master","last_synced_at":"2024-10-29T15:06:44.005Z","etag":null,"topics":["drag-and-drop","draggable","multidrag","react","sortable"],"latest_commit_sha":null,"homepage":"http://sortablejs.github.io/react-sortablejs/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SortableJS.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-03-10T07:18:39.000Z","updated_at":"2024-10-26T21:48:59.000Z","dependencies_parsed_at":"2023-12-06T14:49:41.884Z","dependency_job_id":null,"html_url":"https://github.com/SortableJS/react-sortablejs","commit_stats":{"total_commits":488,"total_committers":43,"mean_commits":"11.348837209302326","dds":0.6188524590163935,"last_synced_commit":"5e6ed8ab656bce48136599d09e7126e8af62bb52"},"previous_names":["cheton/react-sortable"],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SortableJS%2Freact-sortablejs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SortableJS%2Freact-sortablejs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SortableJS%2Freact-sortablejs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SortableJS%2Freact-sortablejs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SortableJS","download_url":"https://codeload.github.com/SortableJS/react-sortablejs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245568565,"owners_count":20636803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["drag-and-drop","draggable","multidrag","react","sortable"],"created_at":"2024-07-31T16:01:22.019Z","updated_at":"2025-03-27T00:31:38.217Z","avatar_url":"https://github.com/SortableJS.png","language":"TypeScript","readme":"# `react-sortablejs`\n\nReact bindings to [SortableJS](https://github.com/SortableJS/Sortable)\n\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n\nPlease note that this is not considered ready for production, as there are still a number of bugs being sent through.\n\n## Features\n\n## Installation\n\n`sortablejs` and `@types/sortablejs` are peer dependencies. The latter is only used if intellisense/typescript is desired.\n\n```shell\nnpm install --save react-sortablejs sortablejs\nnpm install --save-dev @types/sortablejs\n\n# OR\nyarn add react-sortablejs sortablejs\nyarn add -D @types/sortablejs\n```\n\n## Learn\n\nHere is the TLDR of what sortable is:\n\n```md\n- Shopping List: # list of items / sortable. This represents `react-sortablejs`\n  - eggs # list item. These are all the items in the list and are what you move around.\n  - bread # list item\n  - milk # list item\n```\n\n## Usage/Examples\n\n### Function Component\n\n```tsx\nimport React, { FC, useState } from \"react\";\nimport { ReactSortable } from \"react-sortablejs\";\n\ninterface ItemType {\n  id: number;\n  name: string;\n}\n\nexport const BasicFunction: FC = (props) =\u003e {\n  const [state, setState] = useState\u003cItemType[]\u003e([\n    { id: 1, name: \"shrek\" },\n    { id: 2, name: \"fiona\" },\n  ]);\n\n  return (\n    \u003cReactSortable list={state} setList={setState}\u003e\n      {state.map((item) =\u003e (\n        \u003cdiv key={item.id}\u003e{item.name}\u003c/div\u003e\n      ))}\n    \u003c/ReactSortable\u003e\n  );\n};\n```\n\n### Class Component\n\n```tsx\nimport React, { Component } from \"react\";\nimport { ReactSortable } from \"react-sortablejs\";\n\ninterface BasicClassState {\n  list: { id: string; name: string }[];\n}\n\nexport class BasicClass extends Component\u003c{}, BasicClassState\u003e {\n  state: BasicClassState = {\n    list: [{ id: \"1\", name: \"shrek\" }],\n  };\n  render() {\n    return (\n      \u003cReactSortable\n        list={this.state.list}\n        setList={(newState) =\u003e this.setState({ list: newState })}\n      \u003e\n        {this.state.list.map((item) =\u003e (\n          \u003cdiv key={item.id}\u003e{item.name}\u003c/div\u003e\n        ))}\n      \u003c/ReactSortable\u003e\n    );\n  }\n}\n```\n\n## Plugins\n\nSortable has some pretty cool plugins such as MultiDrag and Swap.\n\nBy Default:\n\n- AutoScroll is premounted and enabled.\n- OnSpill is premounted and NOT enabled.\n- MultiDrag and Swap and NOT premounted and NOT enabled\n\nYou must mount the plugin with sortable **ONCE ONLY**.\n\n```tsx\nimport React from \"react\";\nimport { ReactSortable, Sortable, MultiDrag, Swap } from \"react-sortablejs\";\n\n// mount whatever plugins you'd like to. These are the only current options.\nSortable.mount(new MultiDrag(), new Swap());\n\nconst App = () =\u003e {\n  const [state, setState] = useState([\n    { id: 1, name: \"shrek\" },\n    { id: 2, name: \"fiona\" },\n  ]);\n\n  return (\n    \u003cReactSortable\n      multiDrag // enables mutidrag\n      // OR\n      swap // enables swap\n    \u003e\n      {state.map((item) =\u003e (\n        \u003cdiv key={item.id}\u003e{item.name}\u003c/div\u003e\n      ))}\n    \u003c/ReactSortable\u003e\n  );\n};\n```\n\n## Sortable API\n\nFor a comprehensive list of options, please visit https://github.com/SortableJS/Sortable#options.\n\nThose options are applied as follows.\n\n```tsx\nSortable.create(element, {\n  group: \" groupName\",\n  animation: 200,\n  delayOnTouchStart: true,\n  delay: 2,\n});\n\n// --------------------------\n// Will now be...\n// --------------------------\n\nimport React from \"react\";\nimport { ReactSortable } from \"react-sortablejs\";\n\nconst App = () =\u003e {\n  const [state, setState] = useState([\n    { id: 1, name: \"shrek\" },\n    { id: 2, name: \"fiona\" },\n  ]);\n\n  return (\n    \u003cReactSortable\n      // here they are!\n      group=\"groupName\"\n      animation={200}\n      delayOnTouchStart={true}\n      delay={2}\n    \u003e\n      {state.map((item) =\u003e (\n        \u003cdiv key={item.id}\u003e{item.name}\u003c/div\u003e\n      ))}\n    \u003c/ReactSortable\u003e\n  );\n};\n```\n\n## React API\n\n### id, className, style\n\nThese are all default DOM attributes. Nothing special here.\n\n### list\n\nThe same as `state` in `const [ state, setState] = useState([{ id: 1}, {id: 2}])`\n\n`state` must be an array of items, with each item being an object that has the following shape:\n\n```ts\n  /** The unique id associated with your item. It's recommended this is the same as the key prop for your list item. */\n  id: string | number;\n  /** When true, the item is selected using MultiDrag */\n  selected?: boolean;\n  /** When true, the item is deemed \"chosen\", which basically just a mousedown event. */\n  chosen?: boolean;\n  /** When true, it will not be possible to pick this item up in the list. */\n  filtered?: boolean;\n  [property: string]: any;\n```\n\n### setList\n\nThe same as `setState` in `const [ state, setState] = useState([{ id: 1}, {id: 2}])`\n\n### clone\n\nIf you're using `{group: { name: 'groupName', pull: 'clone'}}`, this means you're in 'clone' mode. You should provide a function for this.\n\nCheck out the source code of the clone example for more information. I'll write it here soon.\n\n### tag\n\nReactSortable is a `div` element by default. This can be changed to be any HTML element (for example `ul`, `ol`)\nor can be a React component.\n\nThis value, be it the component or the HTML element, should be passed down under `props.tag`.\n\nLet's explore both here.\n\n#### HTML Element\n\nHere we will use an `ul`. You can use any HTML.\nJust add the string and ReactSortable will use a `ul` instead of a `div`.\n\n```tsx\nimport React, { FC, useState } from \"react\";\nimport { ReactSortable } from \"react-sortablejs\";\n\nexport const BasicFunction: FC = (props) =\u003e {\n  const [state, setState] = useState([{ id: \"1\", name: \"shrek\" }]);\n\n  return (\n    \u003cReactSortable tag=\"ul\" list={state} setList={setState}\u003e\n      {state.map((item) =\u003e (\n        \u003cli key={item.id}\u003e{item.name}\u003c/li\u003e\n      ))}\n    \u003c/ReactSortable\u003e\n  );\n};\n```\n\n#### Custom Component\n\nWhen using a custom component in the `tag` prop, the only component it allows is a `forwardRef` component.\nCurrently, we only support components that use the `React.forwardRef` API.\n\nIf it doesn't have one, you can add one using `React.forwardRef()`.\n\n\u003e todo: Some third-party UI components may have nested elements to create the look they're after.\n\u003e This could be an issue and not sure how to fix it.\n\n```tsx\nimport React, { FC, useState, forwardRef } from \"react\";\nimport { ReactSortable } from \"react-sortablejs\";\n\n// This is just like a normal component, but now has a ref.\nconst CustomComponent = forwardRef\u003cHTMLDivElement, any\u003e((props, ref) =\u003e {\n  return \u003cdiv ref={ref}\u003e{props.children}\u003c/div\u003e;\n});\n\nexport const BasicFunction: FC = (props) =\u003e {\n  const [state, setState] = useState([\n    { id: 1, name: \"shrek\" },\n    { id: 2, name: \"fiona\" },\n  ]);\n\n  return (\n    \u003cReactSortable tag={CustomComponent} list={state} setList={setState}\u003e\n      {state.map((item) =\u003e (\n        \u003cdiv key={item.id}\u003e{item.name}\u003c/div\u003e\n      ))}\n    \u003c/ReactSortable\u003e\n  );\n};\n```\n\n## How does it work?\n\nSortable affects the DOM, adding, and removing nodes/css when it needs to in order to achieve the smooth transitions we all know an love.\nThis component reverses many of its actions of the DOM so React can handle this when the state changes.\n\n## Caveats / Gotchas\n\n### `key !== index`\n\nDO NOT use the index as a key for your list items. Sorting will not work.\n\nIn all the examples above, I used an object with an ID. You should do the same!\n\nI may even enforce this into the design to eliminate errors.\n\n### Nesting\n\n#### Problem\n\nBasically, the child updates the state twice. I'm working on this.\n\n#### What does work?\n\nOur usage indicates that as long as we only move items between lists that don't use the same `setState` function.\n\nI hope to provide an example soon.\n\n#### Solutions\n\nWe don't have anything that works 100%, but here I'd like to spitball some potential avenues to look down.\n\n- Use `onMove` to handle state changes instead of `onAdd`,`onRemove`, etc.\n- Create a Sortable plugin specifically for react-sortbalejs\n","funding_links":[],"categories":["TypeScript","Uncategorized","🧰 React Toolkit"],"sub_categories":["Uncategorized","Drag \u0026 Drop"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSortableJS%2Freact-sortablejs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FSortableJS%2Freact-sortablejs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSortableJS%2Freact-sortablejs/lists"}