feat: Drag-and-drop reordering in outline tree
Summary
This PR implements a complete drag-and-drop system for reordering components in the Puck outline/layer tree using @dnd-kit/react.
Key Features
- Full drag-and-drop support: Drag components to reorder within zones or move between zones
- ZoneNode normalization: Explicit zone labels (e.g., 'items', 'content') shown in tree
- Empty zone visibility: Zone labels remain visible even when empty, enabling first-item insertion
- Smart drop targeting: Clear, intuitive drop positions with automatic redirects for confusing cases
- Expansion state preservation: Components stay expanded when children are removed
- Comprehensive test coverage: 39 unit tests covering flatten, projection, and preview logic
Demo
https://github.com/user-attachments/assets/b50b3b12-b055-447a-aef7-0bc906c1fbbd
Implementation Details
New Files
flatten.ts: Normalizes Puck's nested zone structure into flat array with ZoneNodesprojection.ts: Pure functions for computing drop targets from flattened tree- Test files (
flatten.spec.ts,projection.spec.ts,projection-extended.spec.ts,preview.spec.ts)
Modified Files
LayerTree/index.tsx: Complete rewrite using DragDropProvider with sortable itemsOutline/index.tsx: Simplified to use single unified LayerTree
Technical Approach
- Uses experimental @dnd-kit/react adapter (aligned with Puck's existing DnD implementation)
- ZoneNodes act as explicit drop targets with synthetic IDs (e.g.,
g1::zone::items) - Projection engine computes valid drops, prevents illegal subtree reparenting
- Preview updates show exact drop position during drag
Testing
✅ All 39 unit tests passing, covering:
- ZoneNode emission and hierarchy
- Cross-zone moves and same-zone reorders
- Empty zone handling
- Illegal move detection
- Preview positioning
User-Facing Changes
- Zone labels (like 'items') now visible in outline tree
- Drag handle appears on hover for draggable components
- Visual feedback during drag shows where item will be dropped
- Components can be reordered within zones or moved between zones
- Empty zones remain visible as drop targets
Breaking Changes
None - this is a pure enhancement to existing outline functionality.
Checklist
- [x] Implementation complete
- [x] Unit tests passing (39/39)
- [x] User testing completed
- [x] No breaking changes
- [ ] Documentation updated (if needed)
- [ ] Demo video/GIF added (if requested)
@napter is attempting to deploy a commit to the Puck Team on Vercel.
A member of the Team first needs to authorize it.
@chrisvxd @FedericoBonel what do you think? I'd love some feedback! It is really nice to be able to organize using the treeview - particularly if your page gets complex with lots of nested fields (flex layouts, grid, etc) and the drag and drop on page gets challenging.
The latest updates on your projects. Learn more about Vercel for GitHub.
| Project | Deployment | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| puck-demo | Preview | Comment | Nov 18, 2025 6:04am |
@napter this looks awesome, thank you for doing that. Unfortunately I wasn't able to test or see the proper diff because you made a new directory -- would you be able to flatten the changes?
@chrisvxd - sorry I missed a couple of commits previously. You should be good to review this now.
Hey @napter! Thanks for the updates, I can't seem to be able to test this in the demo app, it should be available there right?