Introducing Motion-Primitives Pro - Advanced components and templates to help you build a website that stands out.

Spinning Text

Easily animate text circularly. Customize the animation with variants and transitions.

Examples

Basic Usage

pre-order • pre-order • pre-order •

Custom Transition

motion-primitives • motion-primitives •

Custom Variants

pre-order • pre-order • pre-order •

Code

'use client';
import { cn } from '@/lib/utils';
import { motion, Transition, Variants } from 'framer-motion';
import React, { CSSProperties } from 'react';

type SpinningTextProps = {
  children: string;
  style?: CSSProperties;
  duration?: number;
  className?: string;
  reverse?: boolean;
  fontSize?: number;
  radius?: number;
  transition?: Transition;
  variants?: {
    container?: Variants;
    item?: Variants;
  };
};

const BASE_TRANSITION = {
  repeat: Infinity,
  ease: 'linear',
};

const BASE_ITEM_VARIANTS = {
  hidden: {
    opacity: 1,
  },
  visible: {
    opacity: 1,
  },
};

export function SpinningText({
  children,
  duration = 10,
  style,
  className,
  reverse = false,
  fontSize = 1,
  radius = 5,
  transition,
  variants,
}: SpinningTextProps) {
  const letters = children.split('');
  const totalLetters = letters.length;

  const finalTransition = {
    ...BASE_TRANSITION,
    ...transition,
    duration: (transition as { duration?: number })?.duration ?? duration,
  };

  const containerVariants = {
    visible: { rotate: reverse ? -360 : 360 },
    ...variants?.container,
  };

  const itemVariants = {
    ...BASE_ITEM_VARIANTS,
    ...variants?.item,
  };

  return (
    <motion.div
      className={cn('relative', className)}
      style={{
        ...style,
      }}
      initial='hidden'
      animate='visible'
      variants={containerVariants}
      transition={finalTransition}
    >
      {letters.map((letter, index) => (
        <motion.span
          aria-hidden='true'
          key={`${index}-${letter}`}
          variants={itemVariants}
          className='absolute left-1/2 top-1/2 inline-block'
          style={
            {
              '--index': index,
              '--total': totalLetters,
              '--font-size': fontSize,
              '--radius': radius,
              fontSize: `calc(var(--font-size, 2) * 1rem)`,
              transform: `
                  translate(-50%, -50%)
                  rotate(calc(360deg / var(--total) * var(--index)))
                  translateY(calc(var(--radius, 5) * -1ch))
                `,
              transformOrigin: 'center',
            } as React.CSSProperties
          }
        >
          {letter}
        </motion.span>
      ))}
      <span className='sr-only'>{children}</span>
    </motion.div>
  );
}

Please add:

Component API

SpinningText

PropTypeDefaultDescription
childrenReactElementThe text content to be animated in a circular motion.
styleCSSProperties{}Custom styles for the text container.
durationnumber10The duration of the full circular rotation animation.
classNamestringA custom class name for the text container.
reversebooleanfalseDetermines if the animation should rotate in reverse.
fontSizenumber1The font size of the text being animated in rem.
radiusnumber5The radius of the circular path for the text animation.
transitionTransitionCustom transition effects for the animation.
variants{ container?: Variants; item?: Variants; }Variants for container and item animations.

Credit

Initiated by @ibtihelbs