Progress
A visual indicator for task completion.
- Chromium
- Firefox
- WebKit
- WCAG 2.2 AA
Preview
import { Progress } from "@moe-ui/registry/components/ui/progress";
import * as React from "react";
import { View } from "react-native";
export default function ProgressPreview() {
const [progress, setProgress] = React.useState(13);
React.useEffect(() => {
const timer = setTimeout(() => setProgress(66), 500);
return () => clearTimeout(timer);
}, []);
return (
<View className="w-full max-w-sm">
<Progress accessibilityLabel="Upload progress" value={progress} />
</View>
);
}
One command, your source
Installation
pnpm dlx @moe-ui/cli@beta add progressUsage
The CLI installs editable source into your application. Import the component from your configured UI directory:
import { Progress } from "@/components/ui/progress";Variants and composition
Compound parts and variants remain regular source in your project, so you can change them without wrapping a package API.
Public API
The canonical source panel below lists every public export, dependency, and the complete implementation shipped by the registry.
Keyboard and accessibility
Status is not communicated by color alone. Add an accessible label or live-region behavior when the message changes dynamically.
Browser support
This beta is release-tested in Chromium, Firefox, and WebKit in light and dark themes. See the web compatibility matrix for the current gate.
Nothing hiddenCanonical source and public exports1 exports
Progress"use client";
import { cn } from "../../lib/utils";
import * as ProgressPrimitive from "@rn-primitives/progress";
import { Platform, View } from "react-native";
import Animated, {
Extrapolation,
interpolate,
useAnimatedStyle,
useDerivedValue,
withSpring,
} from "react-native-reanimated";
function Progress({
className,
value,
indicatorClassName,
...props
}: ProgressPrimitive.RootProps &
React.RefAttributes<ProgressPrimitive.RootRef> & {
indicatorClassName?: string;
}) {
return (
<ProgressPrimitive.Root
className={cn(
"bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",
className,
)}
{...props}
>
<Indicator value={value} className={indicatorClassName} />
</ProgressPrimitive.Root>
);
}
export { Progress };
const Indicator = Platform.select({
web: WebIndicator,
native: NativeIndicator,
default: NullIndicator,
});
type IndicatorProps = {
value: number | undefined | null;
className?: string;
};
function WebIndicator({ value, className }: IndicatorProps) {
if (Platform.OS !== "web") {
return null;
}
return (
<View
className={cn(
"bg-primary h-full w-full flex-1 transition-all",
className,
)}
style={{ transform: `translateX(-${100 - (value ?? 0)}%)` }}
>
<ProgressPrimitive.Indicator className={cn("h-full w-full", className)} />
</View>
);
}
function NativeIndicator({ value, className }: IndicatorProps) {
const progress = useDerivedValue(() => value ?? 0);
const indicator = useAnimatedStyle(() => {
return {
width: withSpring(
`${interpolate(progress.value, [0, 100], [1, 100], Extrapolation.CLAMP)}%`,
{ overshootClamping: true },
),
};
}, [value]);
if (Platform.OS === "web") {
return null;
}
return (
<ProgressPrimitive.Indicator asChild>
<Animated.View
style={indicator}
className={cn("bg-foreground h-full", className)}
/>
</ProgressPrimitive.Indicator>
);
}
function NullIndicator() {
return null;
}