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.

Variants

Variants let you name animation targets and orchestrate them across initial, animate, exit, and while* props. They enable reusable animations and parent-child coordination.

Defining Variants

import { motion } from "motion-solid";

const card = {
  hidden: { opacity: 0, y: 20 },
  visible: {
    opacity: 1,
    y: 0,
    transition: { duration: 0.35 },
  },
  exit: {
    opacity: 0,
    y: -20,
    transition: { duration: 0.2 },
  },
};

<motion.div variants={card} initial="hidden" animate="visible" exit="exit" />;

Props That Accept Variants

These props accept variant labels: initial, animate, exit, whileHover, whileTap, whileFocus, whileInView, whileDrag.

Multiple Labels

Pass an array to merge variants in order:

const card = {
  visible: { opacity: 1, y: 0 },
  highlighted: { scale: 1.05, "box-shadow": "0 4px 12px rgba(0,0,0,0.1)" },
};

<motion.div variants={card} animate={["visible", "highlighted"]} />;

Later variants override earlier ones for conflicting properties.

Function Variants

Variants can be functions that receive (custom, current, velocity):

const item = {
  hidden: { opacity: 0 },
  visible: (custom: number) => ({
    opacity: 1,
    transition: { delay: custom * 0.1 },
  }),
};

<For each={items()}>
  {(item, i) => (
    <motion.div
      variants={variants}
      custom={i()}
      initial="hidden"
      animate="visible"
    />
  )}
</For>;

Function Parameters

  • custom - Value from custom prop or AnimatePresence
  • current - Current motion values
  • velocity - Current velocity of motion values

Returning Labels

Function variants can return a label string, resolved against the same variants map:

const menu = {
  open: { opacity: 1 },
  closed: { opacity: 0 },
  toggle: (isOpen: boolean) => (isOpen ? "open" : "closed"),
};

<motion.div variants={menu} animate="toggle" custom={isOpen()} />;

Orchestration

Parent variants can coordinate child animations:

const container = {
  hidden: { opacity: 0 },
  show: {
    opacity: 1,
    transition: {
      when: "beforeChildren",
      staggerChildren: 0.1,
      delayChildren: 0.2,
    },
  },
};

const item = {
  hidden: { opacity: 0, y: 10 },
  show: { opacity: 1, y: 0 },
};

<motion.ul variants={container} initial="hidden" animate="show">
  <For each={items()}>{() => <motion.li variants={item} />}</For>
</motion.ul>;

Orchestration options:

  • when: "beforeChildren" - Parent finishes before children start
  • when: "afterChildren" - Children finish before parent starts
  • delayChildren - Base delay before children start
  • staggerChildren - Delay between each child
  • staggerDirection - 1 (forward) or -1 (reverse)

Advanced Staggering

Use the stagger function for custom patterns:

import { stagger } from "motion-solid";

const container = {
  show: {
    transition: {
      delayChildren: stagger(0.08, {
        from: "center", // or "first", "last", or index
      }),
    },
  },
};

Inheritance

Children inherit variant labels from ancestors by default:

<motion.ul variants={list} initial="hidden" animate="show">
  {/* Inherits "hidden" and "show" */}
  <motion.li variants={item} />

  {/* Uses its own "peek" instead of inherited "show" */}
  <motion.li variants={item} animate="peek" inherit={false} />
</motion.ul>

Set inherit={false} to opt out of parent variant resolution.

Example: Staggered List

import { motion } from "motion-solid";
import { For } from "solid-js";

const container = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.05,
    },
  },
};

const item = {
  hidden: { opacity: 0, x: -20 },
  visible: { opacity: 1, x: 0 },
};

function StaggeredList(props: { items: string[] }) {
  return (
    <motion.ul variants={container} initial="hidden" animate="visible">
      <For each={props.items}>
        {(text) => <motion.li variants={item}>{text}</motion.li>}
      </For>
    </motion.ul>
  );
}

Example: Expandable Card

const card = {
  collapsed: {
    height: 60,
    transition: { type: "spring", stiffness: 300, damping: 30 },
  },
  expanded: (custom: { height: number }) => ({
    height: custom.height,
    transition: { type: "spring", stiffness: 300, damping: 30 },
  }),
};

function ExpandableCard(props: { content: string }) {
  const [expanded, setExpanded] = createSignal(false);

  return (
    <motion.div
      variants={card}
      initial="collapsed"
      animate={expanded() ? "expanded" : "collapsed"}
      custom={{ height: expanded() ? 200 : 60 }}
      onClick={() => setExpanded((e) => !e)}
    >
      {props.content}
    </motion.div>
  );
}