Text Morph
Animates text by morphing shared letters between words, creating fluid transitions
Examples
Text Morph Button
Text Morph Input
Code
'use client';
import { cn } from '@/lib/utils';
import { AnimatePresence, motion } from 'framer-motion';
import { useMemo, useId } from 'react';
type TextMorphProps = {
children: string;
as?: React.ElementType;
className?: string;
style?: React.CSSProperties;
};
export function TextMorph({
children,
as: Component = 'p',
className,
style,
}: TextMorphProps) {
const uniqueId = useId();
const characters = useMemo(() => {
const charCounts: Record<string, number> = {};
return children.split('').map((char, index) => {
const lowerChar = char.toLowerCase();
charCounts[lowerChar] = (charCounts[lowerChar] || 0) + 1;
return {
id: `${uniqueId}-${lowerChar}${charCounts[lowerChar]}`,
label: index === 0 ? char.toUpperCase() : lowerChar,
};
});
}, [children, uniqueId]);
return (
<Component className={cn(className)} aria-label={children} style={style}>
<AnimatePresence mode='popLayout' initial={false}>
{characters.map((character) => (
<motion.span
key={character.id}
layoutId={character.id}
className='inline-block'
aria-hidden='true'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{
type: 'spring',
stiffness: 280,
damping: 18,
mass: 0.3,
}}
>
{character.label}
</motion.span>
))}
</AnimatePresence>
</Component>
);
}
Please add:
Component API
TextMorph
Prop | Type | Default | Description |
---|---|---|---|
children | string | The text content to be animated. | |
as | keyof JSX.IntrinsicElements | 'p' | The HTML tag to render, defaults to paragraph. |
className | string | undefined | Optional CSS class for styling the component. |
style | React.CSSProperties | undefined | Optional inline styles for the component. |
Credits
Inspired by Family iOS app. See also Family Values