13.01.2026

React’te Animasyonları “Layout” Seviyesinde Yönetmek: FLIP Tekniği ile Akıcı Liste Geçişleri

FLIP tekniğiyle React’te liste sıralama/ekleme/silme animasyonlarını düşük maliyetle ve takılmadan uygulayın.

React’te bir listeyi filtrelediğinizde, sıraladığınızda veya eleman ekleyip sildiğinizde yaşanan “zıplama” hissi çoğu zaman animasyonun yanlış yerde yapılmasından kaynaklanır. CSS transition ile height/top gibi maliyetli özellikleri oynatmak yerine, FLIP (First–Last–Invert–Play) tekniğiyle değişimi “layout” yerine transform üzerinden akıcı hale getirebilirsiniz.

FLIP nedir?

FLIP dört adımda çalışır:

  1. First: Değişiklikten önce elemanların konumlarını ölç.
  2. Last: React ile state’i değiştir, DOM yeni konumlara gelsin.
  3. Invert: Yeni konum ile eski konum arasındaki farkı hesapla ve elemanı ters yönde transform ile eski yerine “geri taşı”.
  4. Play: transform’u animasyonla sıfıra getir; kullanıcı akıcı geçiş görür.

Bu yaklaşım, tarayıcının pahalı layout hesaplarını minimuma indirip GPU dostu transform animasyonu kullanır.

Örnek: Sıralanabilir bir listeyi FLIP ile canlandırmak

Aşağıdaki örnekte, butona basınca liste sıralaması değişiyor. FLIP ile tüm elemanlar yeni yerine “kayarak” gider.

import React, { useLayoutEffect, useRef, useState } from "react";

function useFlip(keys) {
  const rectsRef = useRef(new Map());
  const nodesRef = useRef(new Map());

  const register = (key) => (node) => {
    if (!node) {
      nodesRef.current.delete(key);
      return;
    }
    nodesRef.current.set(key, node);
  };

  // First: render öncesi mevcut konumları sakla
  useLayoutEffect(() => {
    rectsRef.current.clear();
    for (const key of keys) {
      const node = nodesRef.current.get(key);
      if (node) rectsRef.current.set(key, node.getBoundingClientRect());
    }
  });

  // Last + Invert + Play: DOM güncellendikten sonra farkı uygula
  useLayoutEffect(() => {
    for (const key of keys) {
      const node = nodesRef.current.get(key);
      const first = rectsRef.current.get(key);
      if (!node || !first) continue;

      const last = node.getBoundingClientRect();
      const dx = first.left - last.left;
      const dy = first.top - last.top;

      if (dx === 0 && dy === 0) continue;

      node.style.transform = `translate(${dx}px, ${dy}px)`;
      node.style.transition = "transform 0s";

      // next frame: animasyonu başlat
      requestAnimationFrame(() => {
        node.style.transition = "transform 220ms cubic-bezier(.2,.8,.2,1)";
        node.style.transform = "translate(0px, 0px)";
      });
    }
  }, [keys.join("|")]);

  return { register };
}

export default function FlipList() {
  const [items, setItems] = useState([
    { id: "a", label: "React" },
    { id: "b", label: "Vue" },
    { id: "c", label: "Svelte" },
    { id: "d", label: "Solid" },
  ]);

  const keys = items.map((x) => x.id);
  const { register } = useFlip(keys);

  return (
    <div style={{ maxWidth: 360 }}>
      <button
        onClick={() =>
          setItems((prev) => [...prev].reverse())
        }
      >
        Sırayı ters çevir
      </button>

      <ul style={{ listStyle: "none", padding: 0, marginTop: 12 }}>
        {items.map((item) => (
          <li
            key={item.id}
            ref={register(item.id)}
            style={{
              padding: "10px 12px",
              marginBottom: 8,
              border: "1px solid #e5e7eb",
              borderRadius: 8,
              background: "white",
              willChange: "transform",
            }}
          >
            {item.label}
          </li>
        ))}
      </ul>
    </div>
  );
}

Neden useLayoutEffect?

Ölçüm ve transform “tersleme” işlemi, tarayıcı ekrana boyamadan önce yapılırsa flicker (bir frame’lik sıçrama) azalır. Bu yüzden useEffect yerine useLayoutEffect tercih edilir.

Pratik ipuçları

  • Sadece transform/opacity animasyonu yapın: height, top, left gibi özellikler daha pahalıdır.
  • Elemanlara will-change: transform eklemek, sık animasyonlarda yardımcı olabilir.
  • “Silme” animasyonu istiyorsanız, elemanı state’ten hemen kaldırmak yerine kısa süre DOM’da tutup sonra kaldırmayı (exit animation) düşünün.
  • Büyük listelerde ölçüm maliyetini azaltmak için sadece “değişen” aralığı ölçmeye çalışın.

Ne zaman kullanmalı?

  • Drag & drop sıralama (kütüphane kullanmasanız bile),
  • Filtreleme sonrası liste yeniden dizilimi,
  • Kanban kolonlarında kartların yer değiştirmesi,
  • Grid/galeri düzeninde elemanların akıcı geçişi.

FLIP, React’te animasyonu “sonradan eklenen makyaj” olmaktan çıkarıp etkileşimin bir parçası haline getirir: daha az jank, daha çok akıcılık.