# Progress A horizontal bar that fills from left to right to show how complete something is. Use it for upload progress, onboarding checklists, storage usage, or anything that has a percentage value. --- ## Quick example ```csharp new Progress(value: 72) ``` --- ## All the options ```csharp public Progress(int value, string size = "default") ``` | Parameter | What it does | |---|---| | `value` | How filled the bar is, from 0 to 100. Values outside this range are clamped automatically. | | `size` | Height of the bar: `"sm"` (6px), `"default"` (10px), or `"lg"` (16px). | --- ## Real-world examples ### Disk usage inside a Card ```csharp // Pre-render the Progress bar to HTML var w = new System.Buffers.ArrayBufferWriter(); new Progress(value: usedPercent, size: "lg").Render(new HtmxRenderContext(w)); var progressHtml = System.Text.Encoding.UTF8.GetString(w.WrittenSpan); new Card( title: "Storage", content: $"""
Used {usedGb} GB / {totalGb} GB
{progressHtml} """) ``` ### Live progress bar (HTMX polling) Wrap the component in a polling `
` that swaps the fragment every second: ```html
$$ProgressBar$$
``` The handler returns a fresh render of the component with the updated value. The `transition-all` CSS on the fill makes the change smooth. ### Three sizes side by side ```csharp new Progress(value: 40, size: "sm") // compact, good for table rows new Progress(value: 60) // standard new Progress(value: 80, size: "lg") // prominent ``` --- ## How it works Progress is two nested `
` elements. The outer one is the grey track; the inner one is the filled bar. The fill width is set as an inline `style="width: {value}%"` so no JavaScript is required. The `transition-all` class makes the bar animate smoothly when the value changes via an HTMX swap. --- ## Complete page example **`Templates/JobStatusPage.htmx`** ```html

Processing

$$StatusText$$

Progress $$ProgressLabel$$
$$ProgressBar$$ $$DoneAlert$$
``` **`Templates/JobStatusPage.htmx.cs`** ```csharp namespace Htmx.ApiDemo.Templates; public sealed class JobStatusPage : JobStatusPageBase { private readonly byte[] _statusText; private readonly byte[] _progressLabel; private readonly IHtmxComponent _progressBar; private readonly IHtmxComponent _doneAlert; public JobStatusPage(int percent, string statusText) { _statusText = System.Net.WebUtility.HtmlEncode(statusText).ToUtf8Bytes(); _progressLabel = $"{percent}%".ToUtf8Bytes(); _progressBar = new Components.Progress(value: percent); _doneAlert = percent >= 100 ? new Components.Alert(title: "Complete!", description: "Your export is ready.") : HtmxEmpty.Instance; } protected override void RenderStatusText(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_statusText); protected override void RenderProgressLabel(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_progressLabel); protected override void RenderProgressBar(HtmxRenderContext ctx) => _progressBar.Render(ctx.Next()); protected override void RenderDoneAlert(HtmxRenderContext ctx) => _doneAlert.Render(ctx.Next()); } ``` **GET handler with HTMX polling** ```csharp [Handler] [MapGet("/jobs/{jobId}/status")] public static partial class GetJobStatusHandler { public record Query([property: FromRoute] string JobId); private static async Task HandleAsync( Query q, HttpContext ctx, JobQueue jobs, CancellationToken ct) { var job = await jobs.GetAsync(q.JobId, ct); if (job is null) return Results.NotFound(); var page = new JobStatusPage(job.PercentComplete, job.StatusText); // If polling (HTMX partial), only return the progress fragment if (ctx.Request.Headers.ContainsKey("HX-Request")) { // Stop polling when done if (job.PercentComplete >= 100) ctx.Response.Headers.Append("HX-Trigger", "jobComplete"); return await ctx.WriteHtmxPage(page, title: "Processing"); } // Full page load — include polling trigger ctx.Response.Headers.Append("HX-Trigger-After-Settle", """{"startPolling": {"interval": 1000, "target": "#progress-region"}}"""); return await ctx.WriteHtmxPage(page, title: "Processing"); } } ```