← Catalog
No preview yet
Source
121 lines// Radial Progress — a sweeping circular progress ring with a counting number
// in the center and a label. SVG with a 0..100 viewBox, so it's resolution-
// independent for free. Native Remotion (spring + interpolate, frame-driven).
// Generated on the fly via Wireflow's block API.
import {
AbsoluteFill,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
} from 'remotion';
import { loadFont as loadGrotesk } from '@remotion/google-fonts/SpaceGrotesk';
const { fontFamily: GROTESK } = loadGrotesk();
export type RadialProgressProps = {
value: number | string;
label: string;
suffix: string;
ringColor: string;
trackColor: string;
textColor: string;
backgroundColor: string;
};
export default function RadialProgress({
value = 87,
label = 'Goal complete',
suffix = '%',
ringColor = '#5b3df5',
trackColor = '#20203a',
textColor = '#ffffff',
backgroundColor = '#0a0a14',
}: Partial<RadialProgressProps>) {
const frame = useCurrentFrame();
const { fps, durationInFrames } = useVideoConfig();
const target = Math.max(0, Math.min(100, Number(value) || 0));
const grow = spring({
frame: frame - 6,
fps,
config: { damping: 18, stiffness: 90 },
durationInFrames: 42,
});
const exit = interpolate(
frame,
[durationInFrames - 10, durationInFrames - 1],
[1, 0],
{ extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }
);
const R = 38;
const C = 2 * Math.PI * R;
const dashoffset = C * (1 - (target / 100) * grow);
const display = Math.round(target * grow);
return (
<AbsoluteFill
style={{
backgroundColor,
justifyContent: 'center',
alignItems: 'center',
opacity: exit,
fontFamily: GROTESK,
}}
>
<svg viewBox="0 0 100 100" style={{ width: '62%', height: 'auto' }}>
<circle
cx="50"
cy="50"
r={R}
fill="none"
stroke={trackColor}
strokeWidth="7"
/>
<circle
cx="50"
cy="50"
r={R}
fill="none"
stroke={ringColor}
strokeWidth="7"
strokeLinecap="round"
strokeDasharray={C}
strokeDashoffset={dashoffset}
transform="rotate(-90 50 50)"
/>
<text
x="50"
y="49"
fill={textColor}
fontSize="21"
fontWeight="800"
textAnchor="middle"
dominantBaseline="central"
fontFamily={GROTESK}
style={{ fontVariantNumeric: 'tabular-nums' }}
>
{display}
{suffix}
</text>
<text
x="50"
y="67"
fill={textColor}
opacity={0.55}
fontSize="6.4"
fontWeight="600"
letterSpacing="0.6"
textAnchor="middle"
dominantBaseline="central"
fontFamily={GROTESK}
>
{String(label).toUpperCase()}
</text>
</svg>
</AbsoluteFill>
);
}
Schema (Zod)
import { z } from 'zod';
export const Schema = z.object({
value: z
.union([z.number(), z.string()])
.default(87)
.describe('$port:text Progress value 0–100'),
label: z.string().default('Goal complete').describe('$port:text Label'),
suffix: z.string().default('%').describe('Suffix after the number'),
ringColor: z
.string()
.default('#5b3df5')
.describe('$style:color Progress ring color'),
trackColor: z
.string()
.default('#20203a')
.describe('$style:color Track (unfilled) color'),
textColor: z.string().default('#ffffff').describe('$style:color Text color'),
backgroundColor: z
.string()
.default('#0a0a14')
.describe('$style:color Background color'),
});
Radial Progress
radial_progressA sweeping circular progress ring with a counting number in the center and a label. Resolution-independent SVG. Use for completion, goals, scores, or any 0–100 metric.
Workflow Inputs (2)
- Progress value 0–100
valueTEXT - Label
labelTEXT
Config Fields (5)
suffixringColortextColortrackColorbackgroundColor
Meta
- Updated
- 6/4/2026