AniUI

Typing Indicator

Animated typing dots for chat interfaces.

Installation#

npx @aniui/cli add typing-indicator
Web preview — components render natively on iOS & Android
import { TypingIndicator } from "@/components/ui/typing-indicator";

export function MyScreen() {
  const [isTyping, setIsTyping] = useState(true);

  return (
    <View className="gap-3 p-4">
      <ChatBubble variant="received">Hello!</ChatBubble>
      {isTyping && <TypingIndicator />}
    </View>
  );
}

Usage#

app/index.tsx
import { TypingIndicator } from "@/components/ui/typing-indicator";

export function MyScreen() {
  const [isTyping, setIsTyping] = useState(true);

  return (
    <View className="gap-3 p-4">
      <ChatBubble variant="received">Hello!</ChatBubble>
      {isTyping && <TypingIndicator />}
    </View>
  );
}

Props#

PropTypeDefault
className
string
-

Also accepts all View props. Requires react-native-reanimated for bounce animations.

Accessibility#

  • accessibilityLabel="Typing" for screen readers.
  • Animated dots are decorative; the label conveys meaning to assistive technology.

Source#

components/ui/typing-indicator.tsx
import React, { useEffect } from "react";
import { View } from "react-native";
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withRepeat,
  withSequence,
  withTiming,
  withDelay,
} from "react-native-reanimated";
import { cn } from "@/lib/utils";

function Dot({ delay }: { delay: number }) {
  const translateY = useSharedValue(0);

  useEffect(() => {
    translateY.value = withDelay(
      delay,
      withRepeat(withSequence(withTiming(-4, { duration: 300 }), withTiming(0, { duration: 300 })), -1)
    );
  }, [delay, translateY]);

  const style = useAnimatedStyle(() => ({ transform: [{ translateY: translateY.value }] }));

  return <Animated.View style={style} className="h-2 w-2 rounded-full bg-muted-foreground" />;
}

export interface TypingIndicatorProps extends React.ComponentPropsWithoutRef<typeof View> {
  className?: string;
}

export function TypingIndicator({ className, ...props }: TypingIndicatorProps) {
  return (
    <View
      className={cn("flex-row items-center gap-1 px-4 py-2.5 rounded-2xl bg-secondary self-start rounded-bl-sm", className)}
      accessibilityLabel="Typing"
      {...props}
    >
      <Dot delay={0} />
      <Dot delay={150} />
      <Dot delay={300} />
    </View>
  );
}