AniUI

Timeline

Vertical timeline for events, order tracking, and activity feeds.

Installation#

npx @aniui/cli add timeline
Order confirmedMon 9:00 AM

Your order #4821 has been placed

Payment receivedMon 9:01 AM

Visa ending in 4242

Preparing shipmentTue 11:30 AM

Warehouse is packing your items

In transitPending

Estimated delivery Wed

DeliveredPending

Package will arrive at your door

Web preview — components render natively on iOS & Android
import { Timeline, TimelineItem } from "@/components/ui/timeline";

export function MyScreen() {
  return (
    <Timeline>
      <TimelineItem
        title="Order placed"
        description="Your order has been confirmed"
        time="2:00 PM"
        variant="success"
      />
      <TimelineItem
        title="Processing"
        description="Your order is being prepared"
        time="2:15 PM"
        variant="default"
      />
      <TimelineItem
        title="Shipping"
        description="Waiting for pickup"
        time="Pending"
        variant="muted"
        isLast
      />
    </Timeline>
  );
}

Usage#

app/index.tsx
import { Timeline, TimelineItem } from "@/components/ui/timeline";

export function MyScreen() {
  return (
    <Timeline>
      <TimelineItem
        title="Order placed"
        description="Your order has been confirmed"
        time="2:00 PM"
        variant="success"
      />
      <TimelineItem
        title="Processing"
        description="Your order is being prepared"
        time="2:15 PM"
        variant="default"
      />
      <TimelineItem
        title="Shipping"
        description="Waiting for pickup"
        time="Pending"
        variant="muted"
        isLast
      />
    </Timeline>
  );
}

Props#

Timeline#

PropTypeDefault
className
string
-
children
React.ReactNode
-

TimelineItem#

PropTypeDefault
title
string
-
description
string
-
time
string
-
variant
"default" | "success" | "destructive" | "muted"
"default"
icon
React.ReactNode
-
isLast
boolean
false
className
string
-

Accessibility#

  • Vertical event timeline with accessibilityRole="list".
  • Each timeline item is announced as a list item with its content.

Source#

components/ui/timeline.tsx
import React from "react";
import { View, Text } from "react-native";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

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

export function Timeline({ className, children, ...props }: TimelineProps) {
  return (
    <View className={cn("gap-0", className)} accessibilityRole="list" {...props}>
      {children}
    </View>
  );
}

const dotVariants = cva("h-3 w-3 rounded-full z-10", {
  variants: {
    variant: {
      default: "bg-primary",
      success: "bg-green-500",
      destructive: "bg-destructive",
      muted: "bg-muted-foreground",
    },
  },
  defaultVariants: { variant: "default" },
});

export interface TimelineItemProps
  extends React.ComponentPropsWithoutRef<typeof View>,
    VariantProps<typeof dotVariants> {
  className?: string;
  title: string;
  description?: string;
  time?: string;
  icon?: React.ReactNode;
  isLast?: boolean;
}

export function TimelineItem({
  variant,
  className,
  title,
  description,
  time,
  icon,
  isLast,
  ...props
}: TimelineItemProps) {
  return (
    <View className={cn("flex-row", className)} accessibilityRole="listitem" {...props}>
      <View className="items-center mr-3">
        {icon ?? <View className={dotVariants({ variant })} />}
        {!isLast && <View className="flex-1 w-0.5 bg-border mt-1" />}
      </View>
      <View className={cn("flex-1 pb-6", isLast && "pb-0")}>
        <View className="flex-row items-center justify-between">
          <Text className="text-sm font-medium text-foreground">{title}</Text>
          {time && <Text className="text-xs text-muted-foreground">{time}</Text>}
        </View>
        {description && (
          <Text className="text-sm text-muted-foreground mt-1">{description}</Text>
        )}
      </View>
    </View>
  );
}