Auto Height

A container that animates its height to fit its content whenever it changes.

<script setup lang="ts">
import { AutoHeight } from '@animate-ui/vue/primitives/effects/auto-height'
import { AnimatePresence, motion } from 'motion-v'
import { ref } from 'vue'
import { Button } from '@/components/ui/button'

const SHORT = 'Animate UI is a set of animated primitives for Vue, built with Motion.'
const LONG = [
  'Animate UI is a set of animated primitives for Vue, built with Motion.',
  'Swap the import of your reka-ui primitives and your components animate, with exactly the same API.',
  'Its own primitives, like this one, cover the effects reka-ui doesn\'t have.',
]
const swap = {
  initial: { opacity: 0, filter: 'blur(4px)' },
  animate: { opacity: 1, filter: 'blur(0px)' },
  exit: { opacity: 0, filter: 'blur(4px)' },
  transition: { duration: 0.4, ease: 'easeInOut' },
} as const

const expanded = ref(false)
</script>

<template>
  <div class="flex w-full max-w-sm flex-col items-start gap-4">
    <AutoHeight :deps="[expanded]" class="w-full rounded-xl border bg-card p-4 text-sm">
      <AnimatePresence mode="wait" :initial="false">
        <motion.div v-if="expanded" key="long" class="flex flex-col gap-2" v-bind="swap">
          <p v-for="paragraph in LONG" :key="paragraph">
            {{ paragraph }}
          </p>
        </motion.div>
        <motion.p v-else key="short" v-bind="swap">
          {{ SHORT }}
        </motion.p>
      </AnimatePresence>
    </AutoHeight>
    <Button
      variant="outline"
      size="sm"
      :aria-expanded="expanded"
      @click="expanded = !expanded"
    >
      {{ expanded ? 'Show less' : 'Show more' }}
    </Button>
  </div>
</template>

Installation

npm install @animate-ui/vue motion-v
pnpm add @animate-ui/vue motion-v
yarn add @animate-ui/vue motion-v
bun add @animate-ui/vue motion-v

Usage

<script setup lang="ts">
import { AutoHeight } from '@animate-ui/vue/primitives/effects/auto-height';

defineProps<{ expanded: boolean }>();
</script>

<template>
  <AutoHeight :deps="[expanded]">
    <LongContent v-if="expanded" />
    <ShortContent v-else />
  </AutoHeight>
</template>

The content is measured with a ResizeObserver, so the height also follows changes deps doesn't know about (fonts loading, images, a resized window). deps re-measures right away when your state changes.

Animation

  • The height springs to the content's height (stiffness 300, damping 30, no bounce), clipping the content while it moves.
  • With box-sizing: border-box (Tailwind's default), the container's padding and border are included in the measured height.
  • <AnimateUIProvider :tempo> paces the default spring.
  • Under reduced motion, the height changes at once.

API Reference

AutoHeight

PropTypeDefault
as-child?
booleanfalse
deps?
unknown[][]
transition?
Transition{ type: 'spring', stiffness: 300, damping: 30, bounce: 0, restDelta: 0.01 }
animate?
TargetAndTransition | LegacyAnimationControls-
#default
slot-
...props?
Omit<HTMLMotionProps<'div'>, 'animate'>-

Built by Skyleen. The source code is available on GitHub.

Last updated: 10/23/2026