Beta Status
This library is still in early beta and the API is subject to change and may get daily breaking changes. The documentaton may not be up to date with the latest features and include missing or outdated information. You can always create an issue on GitHub or even better a pull request to fix the documentation or add new features.
Layout
Layout animations automatically transition elements when their position or size changes. This works for reordering, resizing, and element repositioning.
Basic Layout
Add layout to animate position and size changes:
import { motion } from "motion-solid";
// Animates when position or size changes
<motion.div layout />;
Layout Types
Control what gets animated:
layout(default) - Position and sizelayout="position"- Position onlylayout="size"- Size onlylayout="preserve-aspect"- Position, size, maintaining aspect ratio
// Only animate position changes
<motion.div layout="position" />
// Only animate size changes
<motion.div layout="size" />
Shared Layout Transitions
Use layoutId to animate between two elements that represent the same entity:
import { motion } from "motion-solid";
import { Show } from "solid-js";
function Card({ expanded, onToggle }) {
return (
<Show
when={expanded()}
fallback={
<motion.div layoutId="card" onClick={onToggle} class="card-small" />
}
>
<motion.div layoutId="card" onClick={onToggle} class="card-large" />
</Show>
);
}
When expanded changes, the element smoothly morphs between the two positions and sizes.
Set layoutCrossfade={false} to disable opacity crossfades.
Solid-Specific: Layout Dependencies
Solid updates the DOM synchronously, so layout animations need explicit dependencies for changes outside standard props.
Automatic Tracking
motion-solid automatically tracks: class, classList, style, textContent, innerHTML.
Manual Dependencies
For other changes, provide layoutDependencies:
const [isOn, setIsOn] = createSignal(false);
<button
class={`flex ${isOn() ? "justify-start" : "justify-end"}`}
onClick={() => setIsOn((v) => !v)}
>
{/* Child position changes when parent class changes */}
<motion.div layout layoutDependencies={[isOn]} class="size-8 rounded-full" />
</button>;
Add dependencies when: a parent's prop change affects child position, derived state influences layout, or non-standard props trigger layout shifts.
Scroll Containers
Mark scroll containers for accurate layout measurements:
<div class="scroll-container" style="overflow: auto">
<motion.div layout layoutScroll>
{/* Layout measurements account for scroll */}
</motion.div>
</div>
layoutScroll- Marks an element as a scroll containerlayoutRoot- Creates an independent layout tree
Example: Reorderable List
import { motion, AnimatePresence } from "motion-solid";
import { For, createSignal } from "solid-js";
function ReorderableList() {
const [items, setItems] = createSignal(["A", "B", "C", "D"]);
const moveUp = (index: number) => {
if (index === 0) return;
setItems((items) => {
const newItems = [...items];
[newItems[index - 1], newItems[index]] = [
newItems[index],
newItems[index - 1],
];
return newItems;
});
};
return (
<ul>
<For each={items()}>
{(item, index) => (
<motion.li
layout
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
{item}
<button onClick={() => moveUp(index())}>Move up</button>
</motion.li>
)}
</For>
</ul>
);
}
Example: Tab Indicator
function Tabs() {
const [active, setActive] = createSignal(0);
const tabs = ["Overview", "Details", "Settings"];
return (
<div class="tabs">
<For each={tabs}>
{(tab, index) => (
<button onClick={() => setActive(index())}>
{tab}
<Show when={active() === index()}>
<motion.div layoutId="underline" class="underline" />
</Show>
</button>
)}
</For>
</div>
);
}
Notes
- Projection transforms sanitize invalid values (
0,NaN,Infinity) before composing CSS transforms - Exiting elements in shared
layoutIdtransitions are marked not-present before handoff - Layout animations work best with
type: "spring"transitions