Home / Snippets / Animation /

List reorder transition

Animate list items to their new positions when order changes — toggle between normal and reversed.

1 Design system tokens
2 Component library
3 Integration tests
4 Deploy pipeline
Experimental
animationexperimental

Quick implementation

/* View Transitions: each item gets a unique name */
.list-item:nth-child(1) { view-transition-name: item-1; }
.list-item:nth-child(2) { view-transition-name: item-2; }
.list-item:nth-child(3) { view-transition-name: item-3; }
.list-item:nth-child(4) { view-transition-name: item-4; }

/* Customize the morph timing */
::view-transition-group(item-1),
::view-transition-group(item-2),
::view-transition-group(item-3),
::view-transition-group(item-4) {
  animation-duration: 0.4s;
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Trigger the transition in JS */
/* document.startViewTransition(() => reorderItems()); */

/* Pure CSS fallback: flex-direction swap */
.list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.list.reversed {
  flex-direction: column-reverse;
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*) {
    animation-duration: 0.01s;
  }
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer building animated sortable lists.

Goal: When a list is reordered (sorted, filtered, reversed), animate each item from its old position to its new position.

Technical constraints:
- Assign a unique view-transition-name to each list item (e.g. item-1, item-2, …).
- Wrap the DOM reorder in document.startViewTransition() so the browser captures before/after snapshots.
- Set animation-duration on ::view-transition-group() for custom timing.
- Use oklch() for all color values — no hex or rgba().
- Use cubic-bezier(0.4, 0, 0.2, 1) for a material-style easing curve.

Framework variant (pick one):
A) Vanilla JS — reorder DOM nodes inside startViewTransition callback, CSS handles animation.
B) React — use startViewTransition in a state setter callback that changes the list order.

Edge cases to handle:
- Respect prefers-reduced-motion: set animation-duration to near-instant.
- Browsers without View Transitions: items snap to new positions — ensure layout still works.
- Dynamic lists: generate view-transition-name from item IDs, not indices, to handle additions/removals.

Return HTML + CSS + JS.

Why this matters in 2026

Animated list reordering previously required JavaScript FLIP libraries or framework-specific transition groups (React <TransitionGroup>, Vue <TransitionGroup>). The View Transitions API makes this a CSS concern: assign each item a view-transition-name, reorder the DOM inside startViewTransition(), and the browser animates each item from its old position to its new one automatically.

The logic

Each list item needs a unique view-transition-name — the browser uses this to match old and new positions. When you call document.startViewTransition(callback), the browser snapshots every named element, runs your callback (which reorders the DOM), then creates ::view-transition-group(name) pseudo-elements that interpolate between the old and new bounding boxes. The result is smooth positional animation without any JavaScript animation code.

Accessibility & performance

The browser performs the animation in compositor layers, so it's GPU-accelerated and doesn't block the main thread. For very long lists (100+ items), limit the number of named elements to the visible viewport to avoid excessive snapshots. Always respect prefers-reduced-motion by setting ::view-transition-group(*) animation-duration to near-zero. Announce the reorder to screen readers via an aria-live region.