Progress

The Progress component displays the completion progress of a task or process. Supports different variants, colors, and animations.

Label and description

Progress accepts the same field props as the input components, rendered outside the track:

<Progress

  

value

=

{

64

}

  

label

=

"Uploading assets"

  

description

=

"12 of 18 files"

  required

  

labelPosition

=

"top"

/>

description is the sublabel beneath the label, error replaces it and renders below the bar, required adds an asterisk (suppress it with withAsterisk={false}), and labelPosition accepts top (default), bottom, left, or right. labelGap tunes the space between the block and the bar, and labelProps / descriptionProps pass through to the underlying <Text> elements. Progress.Root takes the same props, so a segmented bar can be labelled the same way.
Don't confuse this with Progress.Label, which renders text inside a filled section.

Compound components

For multi-part bars, compose Progress.Root with one Progress.Section per segment, and optionally a Progress.Label inside each section:

<Progress.Root

 

size

=

"xl"

>

  

<Progress.Section

 

value

=

{

35

}

 

color

=

"primary"

>

    

<Progress.Label

>

35

%

</Progress.Label

>

  

</Progress.Section

>

  

<Progress.Section

 

value

=

{

28

}

 

color

=

"success"

>

    

<Progress.Label

>

28

%

</Progress.Label

>

  

</Progress.Section

>

</Progress.Root

>

Each section takes its value as a percentage of the whole track, so sections may sum to less than 100 and leave the remainder unfilled. Sections support color, striped, animate, radius, and transitionDuration (inherited from Progress.Root when omitted).

Tooltips

Use the section's own tooltip prop — a string, or a config object for full Tooltip props:

<Progress.Section

 

value

=

{

35

}

 

color

=

"primary"

 

tooltip

=

"Documents — 35%"

 

/>

<Progress.Section

 

value

=

{

28

}

 

color

=

"success"

 

tooltip

=

{

{

label

:

 

'Photos — 28%'

,

withArrow

:

 

true

 

}

}

 

/>

Do not wrap a section in Tooltip yourself. Tooltip renders a wrapper view, which then becomes the flex item inside Progress.Root and sizes itself to its content — collapsing the section's percentage width. The tooltip prop renders the tooltip inside the already-sized section instead. If you do need a manual wrapper, give it the width explicitly: <Tooltip style={{ width: '35%' }}>.
Sections also forward onPress and hover/focus handlers, so they can be made interactive directly.

Vertical orientation

Pass orientation="vertical" to Progress or Progress.Root to fill from the bottom up. Vertical bars have no intrinsic length, so they default to 160 — set length (or the h layout prop) to size them, and size controls the thickness.

<Progress

 

value

=

{

82

}

 

orientation

=

"vertical"

 

length

=

{

120

}

 

size

=

"sm"

 

/>

Basics

Track a single completion percentage. Set transitionDuration so the bar animates its width whenever value changes instead of snapping to it.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

import

 

{

 

Block

,

 

Button

,

 

Progress

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

import

 

{

randomValue

}

 

from

 

'./randomValue'

;

 

const

TRANSITION_MS

=

 

400

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

completion

,

setCompletion

]

 

=

useState

<

number

>

(

50

)

;

  

return

 

(

    

<Block

fullWidth

>

      

<Progress

 

value

=

{

completion

}

 

transitionDuration

=

{

TRANSITION_MS

}

 

/>

      

<Button

 

onPress

=

{

(

)

 

=>

 

setCompletion

(

randomValue

)

}

>

        randomize value

      

</Button

>

    

</Block

>

  

)

;

}

Label and description

Progress takes the same field props as the input components: label, description (the sublabel beneath it), error, required, and labelPosition. The block renders outside the track — use Progress.Label for text drawn inside a filled section.

Loading demo…

import

 

{

 

Block

,

 

Progress

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

    

<Block

 

gap

=

"lg"

fullWidth

>

      

<Progress

 

value

=

{

64

}

 

label

=

"Uploading assets"

 

description

=

"12 of 18 files"

 

/>

 

      

<Progress

        

value

=

{

40

}

        

color

=

"error"

        

label

=

"Sync"

        

description

=

"Retries every 30 seconds"

        

error

=

"Connection lost — retrying"

      

/>

 

      

<Progress

 

value

=

{

82

}

 

label

=

"Storage"

required

labelPosition

=

"left"

 

color

=

"success"

 

/>

 

      

<Progress.Root

 

label

=

"Disk usage"

 

description

=

"Documents, photos, and system files"

>

        

<Progress.Section

 

value

=

{

35

}

 

color

=

"primary"

 

/>

        

<Progress.Section

 

value

=

{

28

}

 

color

=

"success"

 

/>

        

<Progress.Section

 

value

=

{

12

}

 

color

=

"warning"

 

/>

      

</Progress.Root

>

    

</Block

>

  

)

;

}

Advanced

Combine striped and animate to represent indeterminate work.

Loading demo…

import

 

{

useEffect

,

useState

}

 

from

 

'react'

;

import

 

{

 

Block

,

 

Progress

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

import

 

{

describe

}

 

from

 

'./describe'

;

import

 

{

TICK_MS

,

TOTAL_MB

,

stageFor

}

 

from

 

'./stages'

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

value

,

setValue

]

 

=

useState

<

number

>

(

0

)

;

 

  

useEffect

(

(

)

 

=>

 

{

    

// Hold on the completed state for a beat, then start the run over.

    

if

 

(

value

>=

 

100

)

 

{

      

const

restart

=

 

setTimeout

(

(

)

 

=>

 

setValue

(

0

)

,

 

1800

)

;

      

return

 

(

)

 

=>

 

clearTimeout

(

restart

)

;

    

}

 

    

const

tick

=

 

setTimeout

(

(

)

 

=>

 

{

      

setValue

(

(

current

)

 

=>

 

Math

.

min

(

100

,

current

+

 

stageFor

(

current

)

.

speed

)

)

;

    

}

,

TICK_MS

)

;

    

return

 

(

)

 

=>

 

clearTimeout

(

tick

)

;

  

}

,

 

[

value

]

)

;

 

  

const

done

=

value

>=

 

100

;

  

const

stage

=

 

stageFor

(

value

)

;

 

  

return

 

(

    

<Block

fullWidth

>

      

<Progress

        

value

=

{

value

}

        

label

=

{

done

?

 

'Upload complete'

 

:

 

`${stage.label} — ${Math.round(value)}%`

}

        

description

=

{

done

?

 

`18 files · ${TOTAL_MB} MB`

 

:

 

describe

(

value

)

}

        

color

=

{

done

?

 

'success'

 

:

stage

.

color

}

        

striped

=

{

!

done

&&

stage

.

striped

}

        

animate

=

{

!

done

&&

stage

.

striped

}

        

transitionDuration

=

{

TICK_MS

+

 

20

}

        

size

=

"lg"

        

radius

=

"xl"

      

/>

    

</Block

>

  

)

;

}

Compound sections

Compose a multi-part bar from Progress.Root, Progress.Section, and Progress.Label. Each section is sized as a percentage of the track, so sections may sum to less than 100% and leave the remainder unfilled.

Loading demo…

import

 

{

 

Block

,

 

Progress

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

    

<Block

 

gap

=

"lg"

>

      

<Block

 

gap

=

"xs"

>

        

<Text

 

variant

=

"small"

 

color

=

"muted"

>

          

Sections

with inline labels

        

</Text

>

        

<Progress.Root

 

size

=

"xl"

>

          

<Progress.Section

 

value

=

{

35

}

 

color

=

"primary"

>

            

<Progress.Label

>

Docs

</Progress.Label

>

          

</Progress.Section

>

          

<Progress.Section

 

value

=

{

28

}

 

color

=

"success"

>

            

<Progress.Label

>

Media

</Progress.Label

>

          

</Progress.Section

>

          

<Progress.Section

 

value

=

{

15

}

 

color

=

"warning"

>

            

<Progress.Label

>

Other

</Progress.Label

>

          

</Progress.Section

>

        

</Progress.Root

>

        

<Text

 

variant

=

"small"

 

color

=

"muted"

>

          

Sections

take a share of the track

,

so the remaining

22

%

stays unfilled

.

        

</Text

>

      

</Block

>

 

      

<Block

 

gap

=

"xs"

>

        

<Text

 

variant

=

"small"

 

color

=

"muted"

>

          

Striped

and animated sections

        

</Text

>

        

<Progress.Root

 

size

=

"lg"

 

radius

=

"xl"

>

          

<Progress.Section

 

value

=

{

45

}

 

color

=

"primary"

 

/>

          

<Progress.Section

 

value

=

{

25

}

 

color

=

"secondary"

striped animate

/>

        

</Progress.Root

>

        

<Text

 

variant

=

"small"

 

color

=

"muted"

>

          

`striped`

and

`animate`

work per section

,

marking in

-

flight work

.

        

</Text

>

      

</Block

>

    

</Block

>

  

)

;

}

With tooltips

Loading demo…

import

 

{

 

Block

,

 

Progress

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

const

 

SECTIONS

 

=

 

[

  

{

label

:

 

'Documents'

,

value

:

 

34

,

color

:

 

'primary'

as

const

 

}

,

  

{

label

:

 

'Photos'

,

value

:

 

26

,

color

:

 

'success'

as

const

 

}

,

  

{

label

:

 

'Backups'

,

value

:

 

18

,

color

:

 

'warning'

as

const

 

}

]

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

      

<Progress.Root

 

size

=

"xl"

>

        

{

SECTIONS

.

map

(

(

section

)

 

=>

 

(

          

<Progress.Section

            

key

=

{

section

.

label

}

            

value

=

{

section

.

value

}

            

color

=

{

section

.

color

}

            

tooltip

=

{

`${section.label} — ${section.value}%`

}

          

>

            

<Progress.Label

>

{

section

.

value

}

%

</Progress.Label

>

          

</Progress.Section

>

        

)

)

}

      

</Progress.Root

>

  

)

;

}

Example — segments with legend

Custom-colored segments with tooltips and legend.

Loading demo…

import

 

{

 

Block

,

 

ColorSwatch

,

 

Progress

,

 

Row

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

const

 

USAGE

 

=

 

[

  

{

label

:

 

'Documents'

,

value

:

 

32

,

color

:

 

'#4c6ef5'

 

}

,

  

{

label

:

 

'Music'

,

value

:

 

24

,

color

:

 

'#12b886'

 

}

,

  

{

label

:

 

'Code'

,

value

:

 

14

,

color

:

 

'#fab005'

 

}

,

  

{

label

:

 

'Video Games'

,

value

:

 

9

,

color

:

 

'#fa5252'

 

}

]

;

 

const

TOTAL_GB

=

 

500

;

const

used

=

 

USAGE

.

reduce

(

(

sum

,

segment

)

 

=>

sum

+

segment

.

value

,

 

0

)

;

 

const

formatSize

=

 

(

percent

:

 

number

)

 

=>

 

{

  

const

gb

=

 

(

percent

/

 

100

)

 

*

TOTAL_GB

;

  

return

gb

<

 

1

 

?

 

`${Math.round(gb * 1024)} MB`

 

:

 

`${Math.round(gb)} GB`

;

}

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

    

<Block

 

gap

=

"md"

fullWidth

>

      

<Row

 

justify

=

"space-between"

 

align

=

"center"

>

        

<Text

 

weight

=

"600"

>

Project

storage

</Text

>

        

<Text

 

variant

=

"small"

 

color

=

"muted"

>

          

{

formatSize

(

used

)

}

of

{

TOTAL_GB

}

 

GB

used

        

</Text

>

      

</Row

>

 

      

<Progress.Root

 

size

=

"lg"

 

radius

=

"xl"

>

        

{

USAGE

.

map

(

(

segment

)

 

=>

 

(

          

<Progress.Section

            

key

=

{

segment

.

label

}

            

value

=

{

segment

.

value

}

            

color

=

{

segment

.

color

}

            

tooltip

=

{

{

              label

:

 

`${segment.label} — ${formatSize(segment.value)} (${segment.value}%)`

,

              withArrow

:

 

true

            

}

}

          

>

            

<Progress.Label

>

{

formatSize

(

segment

.

value

)

}

</Progress.Label

>

          

</Progress.Section

>

        

)

)

}

      

</Progress.Root

>

 

      

<Block

 

gap

=

"lg"

 

direction

=

"row"

 

justify

=

"center"

>

        

{

USAGE

.

map

(

(

segment

)

 

=>

 

(

          

<Row

 

key

=

{

segment

.

label

}

 

gap

=

"xs"

 

align

=

"center"

>

            

<ColorSwatch

 

color

=

{

segment

.

color

}

 

size

=

{

12

}

 

/>

            

<Text

 

variant

=

"small"

>

{

segment

.

label

}

</Text

>

            

<Text

 

variant

=

"small"

 

color

=

"muted"

>

              

{

segment

.

value

}

%

            

</Text

>

          

</Row

>

        

)

)

}

      

</Block

>

    

</Block

>

  

)

;

}

Vertical orientation

Set orientation="vertical" to fill from the bottom up. Vertical bars have no intrinsic length, so they default to 160 — use length (or h) to size them.

Loading demo…

import

 

{

 

Block

,

 

Progress

,

 

Row

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

const

 

CHANNELS

 

=

 

[

  

{

label

:

 

'Kick'

,

value

:

 

82

,

color

:

 

'primary'

as

const

 

}

,

  

{

label

:

 

'Snare'

,

value

:

 

64

,

color

:

 

'success'

as

const

 

}

,

  

{

label

:

 

'Bass'

,

value

:

 

91

,

color

:

 

'warning'

as

const

 

}

,

  

{

label

:

 

'Vox'

,

value

:

 

47

,

color

:

 

'error'

as

const

 

}

]

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

    

<Block

 

gap

=

"lg"

>

      

<Block

 

gap

=

"sm"

>

        

<Text

 

variant

=

"small"

 

color

=

"muted"

>

          

Vertical

bars fill

from

the bottom up

        

</Text

>

        

<Row

 

gap

=

"md"

 

align

=

"flex-end"

>

          

{

CHANNELS

.

map

(

(

channel

)

 

=>

 

(

            

<Block

 

key

=

{

channel

.

label

}

 

gap

=

"xs"

 

align

=

"center"

>

              

<Progress

                

value

=

{

channel

.

value

}

                

orientation

=

"vertical"

                

length

=

{

120

}

                

size

=

"sm"

                

radius

=

"xl"

                

color

=

{

channel

.

color

}

                

transitionDuration

=

{

600

}

              

/>

              

<Text

 

variant

=

"small"

 

color

=

"muted"

>

                

{

channel

.

label

}

              

</Text

>

            

</Block

>

          

)

)

}

          

<Progress.Root

 

orientation

=

"vertical"

 

length

=

{

140

}

 

size

=

"md"

 

radius

=

"md"

>

            

<Progress.Section

 

value

=

{

40

}

 

color

=

"primary"

 

/>

            

<Progress.Section

 

value

=

{

25

}

 

color

=

"success"

 

/>

            

<Progress.Section

 

value

=

{

15

}

 

color

=

"warning"

striped animate

/>

          

</Progress.Root

>

 

        

</Row

>

      

</Block

>

 

    

</Block

>

  

)

;

}

Scan to open this page on your phonereact-ui-library.com/components/Progress

</>

react-ui-library

100+ accessible, themeable components that work seamlessly across iOS, Android, and Web.

A Platform Blocks product.