Skip to content

Conversation

@morris
Copy link

@morris morris commented Jun 16, 2025

Description

This PR implements fast vertical compaction using a "rising tide" algorithm. For all practical cases, the algorithm has linear time complexity, and requires almost zero additional memory allocations (after cloning and sorting the layout).

While working with larger layouts (200+ items), dragging and dropping items felt sluggish, and I found compact() to be one of the major bottlenecks with lots of recursion and allocations/garbage collection. The following two screenshots from the Chrome profiler should give an idea of the improvement.

Current master:

Screenshot 2025-06-16 at 18 36 51

New algorithm:

Screenshot 2025-06-16 at 18 39 15

Note that the comparison is not fully representative. The recordings were done on random, distinct 300-item layouts (one shot).

Let me know if there's interest, happy to add explanations and/or tests :)

What type of PR is this? (check all applicable)

  • 🍕 Feature
  • 🐛 Bug Fix
  • 📝 Documentation Update
  • 🎨 Style
  • 🧑‍💻 Code Refactor
  • 🔥 Performance Improvements
  • ✅ Test
  • 🤖 Build
  • 🔁 CI
  • 📦 Chore (Release)
  • ⏩ Revert

Related Tickets & Documents

Mobile & Desktop Screenshots/Recordings

Added tests?

  • 👍 yes
  • 🙅 no, because they aren't needed
  • 🙋 no, because I need help

Added to documentation?

  • 📜 README.md
  • 📓 examples
  • 🙅 no documentation needed

@github-actions github-actions bot added core use this label for changes in `lib` directory tests use this label for changes in tests labels Jun 16, 2025
@STRML
Copy link
Collaborator

STRML commented Jun 25, 2025

Hi,

This is very interesting! Thanks for implementing.

This changes the behavior of the default compactor, does it not? Or are the results the same and this is purely performance?

@STRML
Copy link
Collaborator

STRML commented Jun 25, 2025

Additionally, can you see this being implemented horizontally too, and if so, can we remove a bunch of old code? I would be very happy with that.

@STRML STRML mentioned this pull request Jun 25, 2025
17 tasks
@morris
Copy link
Author

morris commented Jun 26, 2025

Hi!

This changes the behavior of the default compactor

I think it does, but hopefully only in subtle ways (weird edge cases). I'll try to quantify this with some tests, let me get back to you.

can you see this being implemented horizontally too

I haven't tried, but I suspect horizontal compaction needs a different approach. While grids can grow vertically indefinitely, the number of columns is constant, so it looks like a (slightly?) separate problem. I'm thinking t might be safer to take it on separately 🤔 would you see this as a show stopper for this PR?

@STRML
Copy link
Collaborator

STRML commented Jun 26, 2025

I don't want to leave the code base with a lot of dead code, so it's fairly high priority. I think this is a measurable improvement and the change is actually easier to read, so I'm excited about what it might bring.

@morris
Copy link
Author

morris commented Jun 27, 2025

I've added two minimal tests illustrating differences in behavior. The first one produces a more compact layout, the second one produces a less compact result (which may be considered a regression) than current master. I think the input layouts are a bit ambiguous though, so not sure if either behavior is "better" or "worse".

Running some randomized tests (roughly same inputs from the new fuzzy tests), I've found the new compaction algorithm to produce equivalent layouts for about 65-75% of inputs. This may be indeed too much of a deviation.

However, I think it comes down more to the user experience while moving items: Does the layout change in unexpected/unintended ways while dragging and dropping? Regarding that, I'm fairly confident it works well (if not better):

Screen.Recording.2025-06-27.at.13.49.07.mov

In the end, it's your call of course :) let me know if you think the changed behavior is good enough. I might take a stab at horizontal compaction in that case

@morris
Copy link
Author

morris commented Sep 1, 2025

Playing with it more, I found the algorithm to be surprisingly powerful as it can support manipulation (move/resize) as well by applying the manipulation, sorting cleverly, and using compaction as a repair step.

I was able to implement a RGL alternative that's more opinionated (less options, e.g. no horizontal compaction) but much, much faster, based on it: https://github.com/morris/fast-grid-layout - may be interesting for you as well :)

@STRML
Copy link
Collaborator

STRML commented Sep 2, 2025

That's pretty cool @morris. I'll spend some time with this again.

I'm a performance junkie and you really whipped my butt with this one.

STRML added a commit that referenced this pull request Dec 8, 2025
This RFC proposes a comprehensive rewrite of react-grid-layout in TypeScript
with a modernized, composable API while maintaining backwards compatibility.

Key changes:
- Pure TypeScript core with no React dependencies in layout algorithms
- Composable interfaces: Compactor, PositionStrategy, DragConfig, ResizeConfig, DropConfig
- Hooks-based API (useGridLayout, useResponsiveLayout, useContainerWidth)
- Fast compaction algorithm (O(n log n) vs O(n²)) - PR #2152
- Drag threshold fix (onDragStart only fires after 3px movement) - PR #1411
- Wrap compact mode support via Compactor interface - PR #1773
- Legacy wrapper for 100% backwards compatibility
- data-grid prop isolated to legacy wrapper only

Breaking changes in v2:
1. onDragStart requires mouse movement (3px threshold)
2. Immutable callback parameters
3. data-grid prop only in legacy wrapper
4. Fast compaction algorithm by default (use legacy* for exact v1 behavior)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@STRML
Copy link
Collaborator

STRML commented Dec 9, 2025

Auto code review

No issues found. Checked for bugs and CLAUDE.md compliance.

Note: This PR was created before the v2 TypeScript rewrite and targets the legacy lib/utils.js codebase. The master branch has since been updated to v2 with TypeScript in src/core/. To merge this performance improvement, the changes would need to be rebased and reimplemented in src/core/compactors.ts.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@STRML
Copy link
Collaborator

STRML commented Dec 9, 2025

Ok this is pretty good.

┌───────────────────────────────────────────────────────────────────────────┐
│                    ⚡ Compactor Performance Comparison                     │
├───────────────────────────────────────────────────────────────────────────┤
│ Items    │ Standard Vertical     │ Fast Vertical         │ Speedup        │
├──────────┼───────────────────────┼───────────────────────┼────────────────┤
│ 50       │             105.03 µs │              17.77 µs │   5.91x faster │
│ 100      │             261.62 µs │              36.20 µs │   7.23x faster │
│ 200      │             928.55 µs │              55.97 µs │  16.59x faster │
│ 500      │               6.56 ms │             141.10 µs │  46.46x faster │
└───────────────────────────────────────────────────────────────────────────┘

STRML added a commit that referenced this pull request Dec 9, 2025
Implements a "rising tide" compaction algorithm that achieves significant
performance improvements for large layouts:

- 50 items: ~6x faster
- 100 items: ~6x faster
- 200 items: ~16x faster
- 500 items: ~45x faster

The algorithm works by:
1. Sorting items by (y, x, static) - top-to-bottom, left-to-right
2. Maintaining a "tide" array tracking the highest occupied row per column
3. Moving each item up to meet the tide (closing gaps)
4. Checking for collisions with static items and adjusting as needed

This avoids the recursive collision resolution of the standard compactor,
reducing complexity from O(n²) to O(n log n).

Usage:
```tsx
import { fastVerticalCompactor } from 'react-grid-layout/extras';

<GridLayout compactor={fastVerticalCompactor} ... />
```

Based on the algorithm from PR #2152 by @morris, migrated to the v2
TypeScript architecture.

Closes #2152
@STRML STRML closed this in #2184 Dec 9, 2025
@STRML
Copy link
Collaborator

STRML commented Dec 9, 2025

This algorithm has been migrated to the v2 TypeScript architecture in #2184. Thanks for the contribution @morris.

pull bot pushed a commit to dlfovvsky/react-grid-layout that referenced this pull request Dec 9, 2025
…react-grid-layout#2184)

Implements a "rising tide" compaction algorithm that achieves significant
performance improvements for large layouts:

- 50 items: ~6x faster
- 100 items: ~6x faster
- 200 items: ~16x faster
- 500 items: ~45x faster

The algorithm works by:
1. Sorting items by (y, x, static) - top-to-bottom, left-to-right
2. Maintaining a "tide" array tracking the highest occupied row per column
3. Moving each item up to meet the tide (closing gaps)
4. Checking for collisions with static items and adjusting as needed

This avoids the recursive collision resolution of the standard compactor,
reducing complexity from O(n²) to O(n log n).

Usage:
```tsx
import { fastVerticalCompactor } from 'react-grid-layout/extras';

<GridLayout compactor={fastVerticalCompactor} ... />
```

Based on the algorithm from PR react-grid-layout#2152 by @morris, migrated to the v2
TypeScript architecture.

Closes react-grid-layout#2152
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core use this label for changes in `lib` directory tests use this label for changes in tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants