Docs/Components/AnimatePresence

AnimatePresence

AnimatePresence keeps a leaving child in the DOM long enough for its exit animation to finish. Wrap the conditional part of the tree, then put exit on the child you want to animate out.

exit
animate-presence-basic.tsxOpen ↗
import { AnimatePresence, motion } from 'motion-solidjs'
import { Show, createSignal } from 'solid-js'

export const meta = {
  slug: 'animate-presence-basic',
  title: 'AnimatePresence — basic',
  category: 'animate-presence',
  description: 'Exit animations when an element unmounts.',
  tag: 'exit',
} as const

export default function AnimatePresenceBasic() {
  const [show, setShow] = createSignal(true)

  return (
    <div class="flex flex-col items-center gap-4">
      <AnimatePresence>
        <Show when={show()}>
          <motion.div
            class="grid h-24 w-24 place-items-center rounded-3xl bg-grad-rose text-3xl"
            initial={{ opacity: 0, scale: 0.4, y: 30 }}
            animate={{ opacity: 1, scale: 1, y: 0 }}
            exit={{ opacity: 0, scale: 0.4, y: -30 }}
            transition={{ type: 'spring', stiffness: 260, damping: 20 }}
          >

          </motion.div>
        </Show>
      </AnimatePresence>
      <button
        onClick={() => setShow((v) => !v)}
        class="rounded-full border border-border bg-card px-4 py-1.5 text-xs text-fg-muted hover:text-fg"
      >
        {show() ? 'Remove' : 'Add'}
      </button>
    </div>
  )
}

Props

  • mode - 'sync' (default), 'wait' (finish exits before entering), or 'popLayout'
  • initial - set to false to skip first-mount animations
  • onExitComplete - fires after all exiting children finish
<AnimatePresence mode="wait" initial={false} onExitComplete={() => undefined}>
  {/* conditional child */}
</AnimatePresence>
mode=wait

Hello

animate-presence-wait.tsxOpen ↗
import { AnimatePresence, motion } from 'motion-solidjs'
import { Show, createSignal } from 'solid-js'

export const meta = {
  slug: 'animate-presence-wait',
  title: 'AnimatePresence — wait',
  category: 'animate-presence',
  description: 'mode="wait" defers the new child until the old one exits.',
  tag: 'mode=wait',
} as const

const slides = ['Hello', 'Bonjour', 'こんにちは', 'Olá', 'Привет']

export default function AnimatePresenceWait() {
  const [i, setI] = createSignal(0)

  return (
    <div class="flex flex-col items-center gap-4">
      <AnimatePresence mode="wait">
        <Show when={slides[i()]} keyed>
          {(slide) => (
            <motion.h2
              class="bg-gradient-to-r from-[#ff5d9e] to-[#ffd166] bg-clip-text text-5xl font-bold text-transparent"
              initial={{ opacity: 0, y: 16, filter: 'blur(8px)' }}
              animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
              exit={{ opacity: 0, y: -16, filter: 'blur(8px)' }}
              transition={{ duration: 0.45, ease: [0.22, 1, 0.36, 1] }}
            >
              {slide}
            </motion.h2>
          )}
        </Show>
      </AnimatePresence>
      <button
        onClick={() => setI((v) => (v + 1) % slides.length)}
        class="rounded-full border border-border bg-card px-4 py-1.5 text-xs text-fg-muted hover:text-fg"
      >
        Next →
      </button>
    </div>
  )
}

See also