LayoutGroup
animate.* components support automatic layout-change animations via three
props: layout, layoutId, and layoutOptions. LayoutGroup scopes
layoutId transitions to a subtree.
layout
Set layout to animate position/size changes caused by reordering,
resizing, or sibling insertion/removal: a FLIP-style transform animation,
composed with whatever transform the element already has from style/
animate/hover/etc.
<animate.div layout style={{ width: expanded ? 300 : 100 }} />
layoutId: shared-element transitions
Give two different elements the same layoutId. When the one carrying it
unmounts (or a new one mounts) with a matching layoutId already active,
the new element automatically plays a FLIP transition from the previous
element's last known position/size to its own (the classic "morph between
elements" pattern used for tab indicators, expanding cards, and shared
images between a list and a detail view).
{selectedTab === 'a' && <animate.div layoutId="indicator" />}
{selectedTab === 'b' && <animate.div layoutId="indicator" />}
layoutOptions sets the transition (spring or timing, options-only, with no
target, since a FLIP transform always settles at identity):
<animate.div layoutId="card" layoutOptions={withSpring({ stiffness: 400, damping: 32 })} />
LayoutGroup
layoutIds are tracked in a single global registry by default, so two
unrelated parts of your app using the same layoutId string would
cross-transition with each other. Wrap a subtree in LayoutGroup to scope
its layoutIds to that subtree instead:
import { LayoutGroup, animate } from 'react-ui-animate';
function TabsA() {
return (
<LayoutGroup>
{tab === 'a1' && <animate.div layoutId="indicator" />}
{tab === 'a2' && <animate.div layoutId="indicator" />}
</LayoutGroup>
);
}
function TabsB() {
return (
<LayoutGroup>
{tab === 'b1' && <animate.div layoutId="indicator" />}
{tab === 'b2' && <animate.div layoutId="indicator" />}
</LayoutGroup>
);
}
TabsA and TabsB both use layoutId="indicator", but each is scoped to its
own LayoutGroup, so they won't cross-animate.
Elements outside any LayoutGroup keep using the global registry, so
existing layoutId usage without a LayoutGroup is unaffected.