Splitting Text

Text that comes in piece by piece, by character, word or line.

<script setup lang="ts">
import { RefreshIcon } from '@hugeicons/core-free-icons'
import { HugeiconsIcon } from '@hugeicons/vue'
import { SplittingText } from '@animate-ui/vue/primitives/texts/splitting'
import { computed, ref } from 'vue'
import { Button } from '@/components/ui/button'

const ANIMATIONS = {
  default: {},
  vibe: {
    initial: { y: 50, scale: 0.5, opacity: 0, x: 50, rotate: 90 },
    animate: { y: 0, scale: 1, opacity: 1, x: 0, rotate: 0 },
    transition: { duration: 0.5, ease: 'easeOut' },
  },
  writing: {
    initial: { y: 10, opacity: 0 },
    animate: { y: 0, opacity: 1 },
    transition: { type: 'spring', bounce: 0, duration: 2 },
  },
} as const

const props = withDefaults(
  defineProps<{
    type?: 'chars' | 'words' | 'lines'
    delay?: number | string
    animation?: keyof typeof ANIMATIONS
  }>(),
  { type: 'chars', delay: 0, animation: 'default' },
)

const run = ref(0)
const animated = computed(() => ({
  delay: Number(props.delay),
  ...ANIMATIONS[props.animation],
}))
</script>

<template>
  <div class="flex flex-col items-center gap-6">
    <SplittingText
      v-if="type === 'lines'"
      :key="`${run}-${type}-${delay}-${animation}`"
      class="text-xl font-semibold"
      type="lines"
      :text="[
        'Introducing Splitting Text component',
        'Made with Motion. Highly customizable and easy to use.',
      ]"
      v-bind="animated"
    />
    <SplittingText
      v-else
      :key="`${run}-${type}-${delay}-${animation}`"
      class="text-4xl font-semibold"
      :type="type"
      text="Splitting Text"
      v-bind="animated"
    />
    <Button variant="outline" size="sm" @click="run++">
      <HugeiconsIcon :icon="RefreshIcon" :stroke-width="2" data-icon="inline-start" />
      Replay
    </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 { SplittingText } from '@animate-ui/vue/primitives/texts/splitting';
</script>

<template>
  <SplittingText text="Splitting Text" type="words" in-view />
</template>

Pass an array of lines with type="lines":

<SplittingText
  type="lines"
  :text="['Introducing Splitting Text', 'Made with Motion.']"
/>

Animation

  • Each piece fades in and slides 150px in from the right over 0.7s on an ease-out, 0.05s apart for characters, 0.2s for words, 0.3s for lines.
  • initial, animate and transition customise every piece.
  • Words never break in the middle; emoji and accented letters stay whole.
  • <AnimateUIProvider :tempo> paces the default transition and stagger.
  • Under reduced motion (or with disable-animation), every piece is in place at once.
  • Screen readers read the text once, not piece by piece.

API Reference

SplittingText

PropTypeDefault
text
string | string[]-
type?
'chars' | 'words' | 'lines''chars'
stagger?
number0.05 (chars), 0.2 (words), 0.3 (lines)
delay?
number0
initial?
TargetAndTransition{ x: 150, opacity: 0 }
animate?
TargetAndTransition{ x: 0, opacity: 1 }
transition?
Transition{ duration: 0.7, ease: 'easeOut' }
disable-animation?
booleanfalse
in-view?
booleanfalse
in-view-margin?
string'0px'
in-view-once?
booleantrue
...props?
Omit<HTMLMotionProps<'div'>, 'children' | 'initial' | 'animate' | 'transition'>-

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

Last updated: 10/23/2026