BTST
PluginsQuickstartDocs
Live Blog

From research to product evaluation

Evaluating a publishing workflow for an app you already own?

See what the BTST Blog plugin adds to an existing React or Next.js app
BTST

Open-source TypeScript features for the React application, data, and deployment you already own.

Released plugins

  • Blog
  • AI Chat
  • CMS
  • Form Builder
  • UI Builder
  • Kanban
  • Comments
  • Media
  • Route Docs
  • OpenAPI
  • Better Auth UI

Resources

  • Quickstart
  • Documentation
  • All plugins
  • Live Blog
  • GitHub (opens in a new tab)
© 2026 BTST. Open source under the MIT License.
AI Chat
September 12, 2026ReactKanban

React Kanban Drag and Drop: Persist Moves and Handle Rejected Writes

Treat a board gesture as a server mutation: distinguish moving from reordering, configure transactions, and verify recovery from stale or unauthorized writes.

React Kanban Drag and Drop: Persist Moves and Handle Rejected Writes

A card moving on screen is only the beginning of a Kanban update. The application still has to authorize the move, persist its position, handle concurrent edits, and recover if the server rejects the change.

BTST's Kanban plugin supplies boards, columns, tasks, and drag-and-drop UI. In @btst/stack@3.0.0, writes go through server operations with distinct permissions and transaction requirements. Integrate those operations before treating a board gesture as saved work.

Distinguish a move from a reorder#

Dragging a task to another column changes its location. Rearranging tasks within a column changes their order. Rearranging the columns changes the board's layout. The server should validate each operation against the resource it changes.

The released permission inventory makes these boundaries explicit:

Gesture or actionRelevant permission
Move a task between columnskanban.task.move
Reorder a column's task setkanban.task.reorder
Reorder a board's columnskanban.column.reorder
Edit task fieldskanban.task.update
Delete a taskkanban.task.delete

Use the plugin's supplied mutation behavior when it fits. If you build a custom drag-and-drop surface, follow the released operation input shapes rather than inventing a generic “save board” payload. A reorder represents a complete set for its scope; it is not an arbitrary list of IDs from several boards.

BTST resolves the task, column, and board facts on the server. Its move implementation rejects a destination column belonging to a different board. If your product needs cross-board transfer, design that as a separate supported operation; do not assume a normal column move provides it.

Configure real transactions#

Kanban writes require an adapter with isolated transaction support. The released code fails with ATOMIC_TRANSACTION_REQUIRED when the adapter does not provide it. Configure transactions using your adapter's documented option; supported Drizzle, Prisma, and Kysely integrations use transaction: true.

Moving a task can change the task itself and the ordering of neighboring tasks. Those updates need to succeed together. A partially persisted move can look correct until a refresh reloads the old order or exposes inconsistent positions.

The released operation implementation also checks snapshots of the resources being changed. Stale-state rejection is an expected outcome to handle when another request changes the same board, not a reason to disable concurrency checks.

Derive access from the resource#

Configure shared server authorization and rules using kanbanPermissions. Decide whether ownership, organization membership, or a specific project role grants each action. The stored board's ownership and organization facts should drive access; a submitted board or organization ID is only a reference to verify.

Collection access needs a separate review. A rule allowing a user to list boards does not, by itself, define which rows a query returns. Check the server query scope as well as direct record access, and test with two users whose visible boards should differ.

Assignee selection is another application boundary. The UI's user resolver and search function should return people the caller is allowed to discover and assign. An assignee avatar is a display feature, not evidence that the assignee has board access.

See the Kanban documentation for provider wiring, user resolution, and permission descriptors. Backend authorization remains necessary even when the interface hides a button.

Recover from a rejected gesture#

If a custom interface applies an optimistic move, retain enough state to roll it back or refetch the affected board when the mutation fails. Show a clear failure state rather than leaving the card in an unsaved position. Avoid blindly replaying a stale reorder payload after newer server changes.

Test these cases before launch:

  1. Move a task, reload the page, and confirm both its destination and surrounding order.
  2. Attempt a move as a user who can read the board but cannot edit it. Confirm persisted state remains unchanged.
  3. Submit a target column from another board. Confirm the operation rejects it.
  4. Reorder from two tabs and handle a rejected or stale response without hiding the server's final state.
  5. Interrupt the network during a write, then refetch before offering a retry. A missing response alone does not prove the write failed.

Do these checks against the real database adapter used in production; an in-memory demo cannot establish database transaction behavior. Evaluate the supplied board UI on the Kanban plugin page, then use framework installation to connect its server and client boundaries.

In This Post

Distinguish a move from a reorderConfigure real transactionsDerive access from the resourceRecover from a rejected gesture