Exit animations
An element that Solid removes is gone at once. Inside <AnimatePresence>, one with an exit stays on the page until that animation has played, then goes.
presence/solid2.tsx
import { AnimatePresence, layout, LazyWardic, w } from "@wardic/solid/unstable/v2"
import { createSignal, For } from "solid-js"
/** Chips that pop in and out: each plays its `exit` before it goes, and the rest close the gap. */
export default function Chips() {
const [chips, setChips] = createSignal([1, 2, 3])
let next = 4
const add = () => setChips((all) => [...all, next++])
const remove = (chip: number) => setChips((all) => all.filter((one) => one !== chip))
return (
<LazyWardic features={[layout]}>
<div class="sample-stack">
<button class="sample-button" onClick={add}>
Add
</button>
<ul class="sample-chips">
{/* popLayout takes a leaving chip out of the row at once, so the rest move up as it goes. */}
<AnimatePresence mode="popLayout">
<For each={chips()}>
{(chip) => (
<w.li class="sample-chip" layout initial={{ opacity: 0, scale: 0.6 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.6 }}>
<button aria-label={`Remove ${chip}`} onClick={() => remove(chip)}>
{chip} ×
</button>
</w.li>
)}
</For>
</AnimatePresence>
</ul>
</div>
</LazyWardic>
)
}presence/solid1.tsx
import { AnimatePresence, layout, LazyWardic, w } from "@wardic/solid"
import { createSignal, For } from "solid-js"
/** Chips that pop in and out: each plays its `exit` before it goes, and the rest close the gap. */
export default function Chips() {
const [chips, setChips] = createSignal([1, 2, 3])
let next = 4
const add = () => setChips((all) => [...all, next++])
const remove = (chip: number) => setChips((all) => all.filter((one) => one !== chip))
return (
<LazyWardic features={[layout]}>
<div class="sample-stack">
<button class="sample-button" onClick={add}>
Add
</button>
<ul class="sample-chips">
{/* popLayout takes a leaving chip out of the row at once, so the rest move up as it goes. */}
<AnimatePresence mode="popLayout">
<For each={chips()}>
{(chip) => (
<w.li class="sample-chip" layout initial={{ opacity: 0, scale: 0.6 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.6 }}>
<button aria-label={`Remove ${chip}`} onClick={() => remove(chip)}>
{chip} ×
</button>
</w.li>
)}
</For>
</AnimatePresence>
</ul>
</div>
</LazyWardic>
)
}presence/motion.tsx
import { AnimatePresence, motion } from "motion/react"
import { useRef, useState } from "react"
/** Chips that pop in and out: each plays its `exit` before it goes, and the rest close the gap. */
export default function Chips() {
const [chips, setChips] = useState([1, 2, 3])
const next = useRef(4)
const add = () => setChips((all) => [...all, next.current++])
const remove = (chip: number) => setChips((all) => all.filter((one) => one !== chip))
return (
<div className="sample-stack">
<button className="sample-button" onClick={add}>
Add
</button>
<ul className="sample-chips">
{/* popLayout takes a leaving chip out of the row at once, so the rest move up as it goes. */}
<AnimatePresence mode="popLayout">
{chips.map((chip) => (
<motion.li key={chip} className="sample-chip" layout initial={{ opacity: 0, scale: 0.6 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.6 }}>
<button aria-label={`Remove ${chip}`} onClick={() => remove(chip)}>
{chip} ×
</button>
</motion.li>
))}
</AnimatePresence>
</ul>
</div>
)
}What counts as a child
Each DOM node directly inside is a child of its own: a <Show>'s element, or each of a <For>'s. For one element that swaps for another, a <Show keyed> makes each value a new child, so the last one leaves as the next one enters:
<AnimatePresence mode="wait">
<Show when={tab()} keyed>
{(name) => <w.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>{name}</w.div>}
</Show>
</AnimatePresence>A key on the element, as in Motion, lets one that comes back while it's still leaving pick up from where its leaver is, rather than enter again.
Modes
"sync"(the default): the leaving and the entering children animate at once."wait": the entering child waits until the leaving one has gone."popLayout": a leaving child is taken out of the layout as it starts to leave, so the others move into its place as it goes, withlayouton them.anchorXandanchorYsay which edge it keeps while it's popped.
Its other props
initial={false}skips the children's entrance at the first render.customis handed to each leaving child'sexitvariants, for an exit that depends on something only known as it leaves, such as the direction a slider went.onExitCompleteis called once every leaving child has gone.propagateplays the exits of nested presences when their parent leaves.
Your own exit
usePresence takes over a child's exit from the code: it stays on the page until you say it can go.
- On Solid 1,
usePresence(element, (safeToRemove, custom) => { … })calls you when it leaves. - On Solid 2,
const presence = usePresence(), given asref={presence}; thenawait presence.next("leave"), andpresence.safeToRemove().
useIsPresent is whether the child is still present, false from the moment it starts to leave.