Back to skills
SKILL.md
2550 Progress D7998082
ASecurity1. [Choosing Progress Style](#choosing-progress-style) 2. [cli_progress_bar() Deep Dive](#cli_progress_bar-deep-dive) 3. [cli_progress_step() Advanced](#cli_progress_step-advanced) 4. [cli_progress_message()](#cli_progress_message) 5. [Progress Variables Reference](#progress-variables-reference) 6. [Format Strings](#format-strings) 7. [Progress Styles](#progress-styles) 8. [Advanced Scenarios](#advanced-scenarios) 9. [Shiny Integration](#shiny-integration) 10. [C-Level Progress](#c-level-progres
- 9 stars
- 0 votes
- 0 copies
- 0 views
- Added October 11, 2026
Works with
Security analysis
100/100npx -y skills add tools-only/X-Skills --skill 2550-progress_d7998082 --agent claude-codeAre you the author of 2550 Progress D7998082?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/tools-only-2550-progress-d7998082)# Progress Indicators
## Table of Contents
1. [Choosing Progress Style](#choosing-progress-style)
2. [cli_progress_bar() Deep Dive](#cli_progress_bar-deep-dive)
3. [cli_progress_step() Advanced](#cli_progress_step-advanced)
4. [cli_progress_message()](#cli_progress_message)
5. [Progress Variables Reference](#progress-variables-reference)
6. [Format Strings](#format-strings)
7. [Progress Styles](#progress-styles)
8. [Advanced Scenarios](#advanced-scenarios)
9. [Shiny Integration](#shiny-integration)
10. [C-Level Progress](#c-level-progress)
11. [Debugging Progress](#debugging-progress)
12. [Performance Considerations](#performance-considerations)
## Choosing Progress Style
cli offers three progress indicator functions, each suited for different scenarios:
### cli_progress_bar()
Use when:
- You know the total number of iterations upfront
- Progress can be measured as a percentage
- Operation involves loops or iterating over collections
- Users need ETA and completion estimates
```r
# Good use case
process_files <- function(files) {
cli_progress_bar("Processing", total = length(files))
for (file in files) {
process(file)
cli_progress_update()
}
}
```
### cli_progress_step()
Use when:
- Progress happens in discrete named steps
- Total work is unknown or variable
- Steps have different durations
- You want automatic success/failure indicators
```r
# Good use case
deploy_app <- function() {
cli_progress_step("Building assets")
build_assets()
cli_progress_step("Running tests")
run_tests()
cli_progress_step("Deploying to server")
deploy()
}
```
### cli_progress_message()
Use when:
- Showing a simple status message
- No quantifiable progress to track
- Operation duration is uncertain
- Want a spinner without step semantics
```r
# Good use case
cli_progress_message("Waiting for API response...")
response <- long_running_api_call()
```
## cli_progress_bar() Deep Dive
### Basic Parameters
```r
cli_progress_bar(
name = NULL, # Progress bar name/label
status = NULL, # Additional status text
type = "iterator", # Bar type: "iterator", "tasks", "download", "custom"
total = NA, # Total number of items (NA for unknown)
format = NULL, # Custom format string
format_done = NULL, # Format when complete
format_failed = NULL, # Format when failed
clear = TRUE, # Clear bar when done
current = TRUE, # Show current position
auto_terminate = TRUE, # Auto-close when function exits
.auto_close = TRUE # Deprecated, use auto_terminate
)
```
### Type Parameter Examples
```r
# Iterator (default) - for loops
cli_progress_bar("Processing items", total = 100, type = "iterator")
# Tasks - for discrete tasks
cli_progress_bar("Completing tasks", total = 5, type = "tasks")
# Download - for file downloads
cli_progress_bar("Downloading", total = file_size, type = "download")
# Custom - for user-defined formats
cli_progress_bar(
format = "Working {cli::pb_bar} {cli::pb_percent}",
total = 100,
type = "custom"
)
```
### Auto-Termination Behavior
Progress bars automatically close when the calling function exits:
```r
process_data <- function(data) {
cli_progress_bar("Processing", total = nrow(data))
for (i in seq_len(nrow(data))) {
process_row(data[i, ])
cli_progress_update()
}
# Bar automatically closes here when function returns
}
# Manual control if needed
process_data_manual <- function(data) {
id <- cli_progress_bar("Processing", total = nrow(data))
for (i in seq_len(nrow(data))) {
process_row(data[i, ])
cli_progress_update(id = id)
}
cli_progress_done(id = id) # Explicit close
}
```
### Clearing Behavior
Control whether progress bars remain visible after completion:
```r
# Clear after completion (default)
cli_progress_bar("Working", total = 100, clear = TRUE)
for (i in 1:100) {
Sys.sleep(0.01)
cli_progress_update()
}
# Bar disappears when done
# Keep visible after completion
cli_progress_bar("Working", total = 100, clear = FALSE)
for (i in 1:100) {
Sys.sleep(0.01)
cli_progress_update()
}
# Bar remains showing 100% completion
```
### Dynamic Updates
Update progress with optional status messages:
```r
process_files <- function(files) {
cli_progress_bar("Processing files", total = length(files))
for (i in seq_along(files)) {
file <- files[[i]]
# Update with status
cli_progress_update(
status = sprintf("Current: %s", basename(file))
)
process_file(file)
}
}
# Update multiple items at once
cli_progress_update(inc = 5) # Increment by 5
cli_progress_update(set = 50) # Set to specific value
```
## Format Strings
### Default Formats
```r
# Iterator format (default)
# "{cli::pb_spin} {cli::pb_name} {cli::pb_bar} {cli::pb_percent} | ETA: {cli::pb_eta}"
# Download format
# "Downloaded {cli::pb_current_bytes}/{cli::pb_total_bytes} ({cli::pb_rate_bytes}/s) | ETA: {cli::pb_eta}"
# Tasks format
# "{cli::pb_name} {cli::pb_current}/{cli::pb_total} | ETA: {cli::pb_eta}"
```
### Custom Format Examples
```r
# Simple percentage only
cli_progress_bar(
"Working",
total = 100,
format = "{cli::pb_name} {cli::pb_percent}"
)
# With ETA and rate
cli_progress_bar(
"Processing",
total = 1000,
format = "{cli::pb_bar} {cli::pb_current}/{cli::pb_total} [{cli::pb_eta} @ {cli::pb_rate}]"
)
# With elapsed time
cli_progress_bar(
"Running",
total = 50,
format = "{cli::pb_spin} {cli::pb_name} {cli::pb_percent} | Elapsed: {cli::pb_elapsed}"
)
# Download-style with bytes
cli_progress_bar(
"Downloading data.zip",
total = 1024^3, # 1 GB
format = paste0(
"{cli::pb_current_bytes}/{cli::pb_total_bytes} ",
"({cli::pb_percent}) | ",
"{cli::pb_rate_bytes}/s | ",
"ETA: {cli::pb_eta}"
)
)
# Custom status display
cli_progress_bar(
format = "{cli::pb_bar} {cli::pb_status}"
)
```
### Completion and Failure Formats
```r
cli_progress_bar(
"Processing",
total = 100,
format = "Working: {cli::pb_bar} {cli::pb_percent}",
format_done = "Completed {cli::pb_total} items in {cli::pb_elapsed}",
format_failed = "Failed after processing {cli::pb_current} items"
)
# Trigger failure
for (i in 1:50) {
if (i == 30) {
cli_progress_done(result = "failed")
break
}
cli_progress_update()
}
```
## Progress Variables Reference
### Basic Progress Variables
```r
# Current position
cli::pb_current # Current iteration number (e.g., 45)
# Total work
cli::pb_total # Total iterations (e.g., 100)
# Percentage
cli::pb_percent # Completion percentage (e.g., "45%")
# Visual bar
cli::pb_bar # Progress bar visualization (e.g., "=========> ")
# Spinner
cli::pb_spin # Animated spinner character
# Name and status
cli::pb_name # Progress bar name
cli::pb_status # Current status message
```
### Timing Variables
```r
# Time elapsed
cli::pb_elapsed # Time since start (e.g., "2m 30s")
cli::pb_elapsed_raw # Elapsed time in seconds (e.g., 150.234)
cli::pb_elapsed_clock # Clock time format (e.g., "02:30")
# Time remaining
cli::pb_eta # Estimated time remaining (e.g., "1m 15s")
cli::pb_eta_raw # ETA in seconds (e.g., 75.0)
cli::pb_eta_str # ETA as string (e.g., "ETA: 1m 15s")
# Rate
cli::pb_rate # Items per second (e.g., "0.6/s")
cli::pb_rate_raw # Raw rate value (e.g., 0.6)
```
### Byte-Based Variables
For download/upload progress bars:
```r
# Current bytes
cli::pb_current_bytes # Formatted current (e.g., "45.2 MB")
# Total bytes
cli::pb_total_bytes # Formatted total (e.g., "100 MB")
# Rate
cli::pb_rate_bytes # Bytes per second (e.g., "2.1 MB/s")
```
### Advanced Variables
```r
# Tick information
cli::pb_tick # Current tick number
cli::pb_tick_rate # Ticks per second
# Timestamps
cli::pb_start # Start timestamp (POSIXct)
cli::pb_timestamp # Current timestamp (POSIXct)
# Extra data
cli::pb_extra # User-defined extra data
```
## cli_progress_step() Advanced
### Basic Usage
```r
deploy_package <- function() {
cli_progress_step("Checking package")
check_result <- check_package()
cli_progress_step("Building package")
build_result <- build_package()
cli_progress_step("Uploading to CRAN")
upload_result <- upload_package()
}
```
### Spinner Customization
```r
# Use different spinner style
options(cli.spinner = "dots")
cli_progress_step("Processing")
# Available spinners
options(cli.spinner = "line") # Classic line spinner
options(cli.spinner = "dots") # Dots
options(cli.spinner = "dots2") # Alternative dots
options(cli.spinner = "dots3") # More dots
options(cli.spinner = "dots12") # 12-frame dots
options(cli.spinner = "arrow") # Rotating arrow
options(cli.spinner = "bouncingBar") # Bouncing bar
options(cli.spinner = "clock") # Clock hands
# Custom spinner frames
options(cli.spinner = list(
interval = 100,
frames = c("◐", "◓", "◑", "◒")
))
```
### Multiple Concurrent Steps
Progress steps can be nested:
```r
process_projects <- function(projects) {
cli_progress_step("Processing {length(projects)} projects")
for (proj in projects) {
cli_progress_step("Building {proj}")
build_project(proj)
cli_progress_step("Testing {proj}")
test_project(proj)
cli_progress_step("Deploying {proj}")
deploy_project(proj)
}
}
```
### Dynamic Message Updates
Update step messages while they're running:
```r
process_with_details <- function(files) {
id <- cli_progress_step("Processing files")
for (i in seq_along(files)) {
cli_progress_update(
id = id,
status = sprintf("File %d/%d: %s", i, length(files), basename(files[i]))
)
process_file(files[i])
}
cli_progress_done(id = id)
}
```
### Success/Failure Termination
```r
deploy_with_status <- function() {
tryCatch({
cli_progress_step("Building application")
build_app()
cli_progress_step("Running tests")
test_result <- run_tests()
if (!test_result$passed) {
cli_progress_done(result = "failed", msg_failed = "Tests failed!")
return(FALSE)
}
cli_progress_step("Deploying")
deploy()
cli_progress_done(result = "done", msg_done = "Deployment successful!")
TRUE
}, error = function(e) {
cli_progress_done(result = "failed", msg_failed = "Deployment failed: {e$message}")
FALSE
})
}
```
### Custom Success/Failure Messages
```r
id <- cli_progress_step(
"Processing data",
msg_done = "Data processed successfully",
msg_failed = "Data processing failed"
)
# Mark as done
cli_progress_done(id = id, result = "done")
# Or mark as failed
cli_progress_done(id = id, result = "failed")
```
## cli_progress_message()
Simple progress messages with automatic spinners:
```r
# Basic usage
cli_progress_message("Loading configuration...")
config <- load_config()
# With explicit cleanup
id <- cli_progress_message("Waiting for server...")
wait_for_server()
cli_progress_done(id = id)
# Updating message
id <- cli_progress_message("Connecting...")
cli_progress_update(id = id, status = "Authenticating...")
cli_progress_update(id = id, status = "Connected!")
cli_progress_done(id = id)
```
## Progress Styles
cli includes several built-in progress bar styles:
```r
# Classic style (default)
options(cli.progress_bar_style = "classic")
# Unicode style (requires Unicode support)
options(cli.progress_bar_style = "unicode")
# ASCII style (for limited terminals)
options(cli.progress_bar_style = "ascii")
# Custom style
options(cli.progress_bar_style = list(
complete = "=",
incomplete = " ",
current = ">",
width = 40
))
```
## Advanced Scenarios
### Nested Progress Bars
```r
process_datasets <- function(datasets) {
cli_progress_bar("Processing datasets", total = length(datasets))
for (dataset in datasets) {
# Outer progress updates
cli_progress_update()
# Inner progress bar
n_rows <- nrow(dataset)
cli_progress_bar("Processing rows", total = n_rows)
for (i in seq_len(n_rows)) {
process_row(dataset[i, ])
cli_progress_update() # Updates inner bar
}
# Inner bar auto-closes
}
# Outer bar auto-closes
}
```
### Progress with Parallel Code
Progress bars in parallel contexts require special handling:
```r
library(foreach)
library(doParallel)
# Not recommended - doesn't work well
process_parallel_bad <- function(items) {
cli_progress_bar("Processing", total = length(items))
foreach(item = items) %dopar% {
result <- process_item(item)
cli_progress_update() # Won't work across processes
result
}
}
# Better approach - update after each completion
process_parallel_better <- function(items) {
cli_progress_bar("Processing", total = length(items))
results <- foreach(item = items) %dopar% {
process_item(item)
}
# Update in main thread
for (i in seq_along(results)) {
cli_progress_update()
}
results
}
# Best approach - use progress updates between parallel batches
process_parallel_best <- function(items, batch_size = 10) {
batches <- split(items, ceiling(seq_along(items) / batch_size))
cli_progress_bar("Processing", total = length(items))
results <- list()
for (batch in batches) {
batch_results <- foreach(item = batch) %dopar% {
process_item(item)
}
results <- c(results, batch_results)
cli_progress_update(inc = length(batch))
}
results
}
```
### Progress in Loops vs Mapping
```r
# Traditional loop with progress
process_loop <- function(items) {
cli_progress_bar("Processing", total = length(items))
results <- vector("list", length(items))
for (i in seq_along(items)) {
results[[i]] <- process_item(items[[i]])
cli_progress_update()
}
results
}
# Using cli_progress_along()
process_along <- function(items) {
results <- lapply(cli_progress_along(items, "Processing"), function(i) {
process_item(items[[i]])
})
results
}
# Using purrr with progress
library(purrr)
process_purrr <- function(items) {
cli_progress_bar("Processing", total = length(items))
map(items, function(item) {
result <- process_item(item)
cli_progress_update()
result
})
}
```
### Progress Output vs Regular CLI Output
Progress bars interact with other cli output:
```r
process_with_messages <- function(items) {
cli_progress_bar("Processing", total = length(items))
for (i in seq_along(items)) {
result <- process_item(items[[i]])
# Regular cli messages work alongside progress
if (result$warnings) {
cli_alert_warning("Item {i} had warnings")
}
cli_progress_update()
}
cli_alert_success("Processing complete")
}
# Progress-aware output functions
process_with_progress_output <- function(items) {
cli_progress_bar("Processing", total = length(items))
for (item in items) {
# These respect progress bars
cli_progress_output(cli_text("Processing {item}"))
process_item(item)
cli_progress_update()
}
}
```
### Unknown Total
Progress bars can run without knowing the total:
```r
process_stream <- function(conn) {
cli_progress_bar("Processing stream", total = NA)
while (length(line <- readLines(conn, n = 1)) > 0) {
process_line(line)
cli_progress_update()
}
# Shows spinner and count, no percentage
}
# Update total when it becomes known
process_dynamic <- function() {
cli_progress_bar("Discovering files", total = NA)
files <- find_files()
# Update total
cli_progress_update(total = length(files))
for (file in files) {
process_file(file)
cli_progress_update()
}
}
```
## Shiny Integration
Progress indicators in Shiny applications require special consideration:
```r
library(shiny)
ui <- fluidPage(
actionButton("process", "Process Data")
)
server <- function(input, output, session) {
observeEvent(input$process, {
# Use Shiny's progress API
withProgress(message = "Processing", {
# cli progress works but won't show in Shiny UI
cli_progress_bar("Internal progress", total = 100)
for (i in 1:100) {
Sys.sleep(0.01)
# Update Shiny progress
incProgress(1/100)
# cli progress (for logs)
cli_progress_update()
}
})
})
}
# Better: Use cli in Shiny with proper output
server <- function(input, output, session) {
observeEvent(input$process, {
# Redirect cli output to console
withProgress(message = "Processing", {
# cli progress shows in R console, not Shiny UI
process_data()
})
})
}
```
## C-Level Progress
For package developers using C/C++:
```r
# In R
process_with_c <- function(data) {
cli_progress_bar("Processing in C", total = nrow(data))
.Call(C_process_data, data, environment())
}
# In C code (using cli's C API)
# #include <cli/progress.h>
#
# SEXP C_process_data(SEXP data, SEXP progress_env) {
# R_xlen_t n = Rf_xlength(data);
#
# for (R_xlen_t i = 0; i < n; i++) {
# // Process data[i]
#
# // Update progress from C
# cli_progress_update(progress_env);
# }
#
# return R_NilValue;
# }
```
Note: C-level progress requires the cli package C headers and proper linking.
## Debugging Progress
### Common Issues
```r
# Issue: Progress bar not updating
# Problem: Not calling cli_progress_update()
for (i in 1:100) {
process(i)
# Missing: cli_progress_update()
}
# Issue: Multiple progress bars interfering
# Solution: Store IDs and update correct bar
id1 <- cli_progress_bar("Task 1", total = 10)
id2 <- cli_progress_bar("Task 2", total = 20)
cli_progress_update(id = id1)
cli_progress_update(id = id2)
# Issue: Progress bar not visible
# Check: Is stderr redirected? Is output captured?
# Progress bars use stderr by default
# Issue: Progress bar flickers
# Problem: Output mixing with progress
# Solution: Use cli_progress_output()
cli_progress_output(cli_alert_info("Status update"))
```
### Testing Progress Bars
```r
test_that("progress bar updates correctly", {
# Mock progress to test without output
mockery::stub(my_function, "cli_progress_bar", NULL)
mockery::stub(my_function, "cli_progress_update", NULL)
result <- my_function()
expect_true(result)
})
# Test with captured output
test_that("progress messages are correct", {
output <- capture.output({
process_data(test_data)
})
# Note: Progress bars may not appear in captured output
# Consider testing function logic separately
})
```
### Disabling Progress
```r
# Disable all progress indicators
options(cli.progress_show_after = Inf)
# Or use show_after parameter
cli_progress_bar("Working", total = 100, show_after = Inf)
# Conditional progress
process_data <- function(data, verbose = TRUE) {
if (verbose) {
cli_progress_bar("Processing", total = nrow(data))
}
for (i in seq_len(nrow(data))) {
process_row(data[i, ])
if (verbose) cli_progress_update()
}
}
```
## Performance Considerations
### Update Frequency
```r
# Bad: Update too frequently (slows down loop)
for (i in 1:1e6) {
fast_operation(i)
cli_progress_update() # 1 million updates!
}
# Better: Update periodically
cli_progress_bar("Processing", total = 1e6)
for (i in 1:1e6) {
fast_operation(i)
if (i %% 1000 == 0) cli_progress_update(set = i)
}
# Best: Use show_after to delay display
cli_progress_bar(
"Processing",
total = 1e6,
show_after = 2 # Only show if takes >2 seconds
)
```
### Overhead
Progress bars add minimal overhead, but consider:
```r
# Negligible overhead for slow operations
process_files <- function(files) {
cli_progress_bar("Processing", total = length(files))
for (file in files) {
slow_operation(file) # 1 second per file
cli_progress_update() # <1ms overhead
}
}
# Noticeable overhead for fast operations
process_numbers <- function(n = 1e6) {
cli_progress_bar("Processing", total = n)
for (i in 1:n) {
fast_operation(i) # 1μs per operation
cli_progress_update() # 1ms overhead - 1000x slower!
}
}
# Solution: Batch updates
process_numbers_fast <- function(n = 1e6, update_every = 1000) {
cli_progress_bar("Processing", total = n)
for (i in 1:n) {
fast_operation(i)
if (i %% update_every == 0) {
cli_progress_update(set = i)
}
}
}
```
### Terminal Performance
```r
# Progress bars can be slow on Windows
# Use ASCII style for better performance
if (.Platform$OS.type == "windows") {
options(cli.progress_bar_style = "ascii")
}
# Disable Unicode for remote sessions
if (Sys.getenv("SSH_CONNECTION") != "") {
options(cli.unicode = FALSE)
}
```
Attribution
Comments
Loading comments…