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 size
  • layout="position" - Position only
  • layout="size" - Size only
  • layout="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 container
  • layoutRoot - 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 layoutId transitions are marked not-present before handoff
  • Layout animations work best with type: "spring" transitions