Multi-screen forms are wonderful right up until the moment your user gets lost. When someone is filling in an event request that spans seven screens — event details, logistics, food, materials, communication, and a final overview — they inevitably start wondering: how much more of this is there? A progress bar answers that question before it is even asked, and the good news is that you do not need a fancy control or a custom component to build one. Three basic controls and one variable will do the job.
In this post I will walk you through exactly how the progress bar in my Events app works, break it down into small pieces so you can rebuild it yourself, and then share a few improvements I would make if I were building it again from scratch.

The idea in one sentence
A progress bar is just a coloured rectangle sitting on top of a grey (or in my case, yellow) rectangle, where the top rectangle grows wider as you move forward. That is genuinely all it is. Everything else is styling.
To make the top rectangle grow, we need to know which step the user is on. We store that in a global variable called gblStep, and we let a tiny bit of maths handle the width.
The three ingredients
On every screen of the form, you place three controls:
recTrack– the background track (the full-width bar that represents the whole journey).recFill– the fill (the coloured part that shows how far you have come).Label2– a text label that spells it out, e.g. “Step 3 of 7”.
Let’s build each one.
Step 1 – Tell the app which step you are on
The single most important part of this whole thing is one line. On the OnVisible property of each screen, set the step number:
powerfx
// On the first screen (NewEventScreen):
Set(gblStep, 1)
// On the second screen (EventDetailsScreen):
Set(gblStep, 2)
// ...and so on, up to the overview screen:
Set(gblStep, 7)
OnVisible runs every time a screen appears, so no matter whether the user arrives by clicking Next, or comes back with a Back button, gblStep is always correct for the screen they are looking at. That is exactly the behaviour we want.
Because gblStep is a global variable (set with Set, not UpdateContext), it is available on every screen without any extra work. Set it once per screen, use it everywhere.
Step 2 – Draw the track
Add a Rectangle control and name it recTrack. This is the full-width bar. Give it these properties:
powerfx
Fill: RGBA(255, 191, 0, 1) // a warm amber
Height: 10
Width: 560
X: 403
Y: 149
The exact colours and coordinates do not matter — pick whatever fits your design. What does matter is that this rectangle represents 100% of the journey. It never changes size.
Step 3 – Draw the fill (the clever bit)
Add a second Rectangle on top of the first, name it recFill, and here is where the magic happens. Instead of giving it a fixed width, we calculate the width based on the current step:
powerfx
Fill: RGBA(54, 176, 75, 1) // green = progress
Height: 10
Width: recTrack.Width * (gblStep / 7)
X: recTrack.X
Y: recTrack.Y
Look closely at the Width formula:
powerfx
recTrack.Width * (gblStep / 7)
gblStep / 7gives us a fraction between0and1. On step 1 that’s1/7 ≈ 0.14, on step 4 it’s4/7 ≈ 0.57, on step 7 it’s7/7 = 1.- We multiply that fraction by the full track width, so the fill is always the correct proportion of the whole bar.
Notice that recFill borrows its X and Y straight from recTrack (X: recTrack.X). This is a small habit worth adopting: instead of typing 403 twice, reference the other control. Move the track later and the fill follows automatically — one less thing to keep in sync.
Step 4 – Add the label
Finally, a plain Label so the number is spelled out for people who prefer words to bars:
powerfx
Text: "Step " & gblStep & " of 7"
Font: Font.'Open Sans'
Size: 10
X: 403
Y: 159
That & is Power Fx’s way of gluing text together, so on the third screen this reads “Step 3 of 7”.
And that is the entire progress bar. Copy those three controls onto each of your screens, make sure every screen sets its own gblStep in OnVisible, and you are done. The bar fills itself in as the user moves through the form.
Improvements I would make next time
The version above works perfectly, and if you are just getting started I would genuinely encourage you to build it exactly like this first — it is easy to understand and easy to debug. But once it clicks, here are the upgrades that turn a good progress bar into a great one.
1. Turn it into a reusable component
Right now the three controls are copy-pasted onto all seven screens. That means if I ever want to change the colour, the height, or the corner radius, I have to do it seven times — and the eighth time I forget one and it looks broken.
The fix is a Canvas Component. Build the track, fill, and label once inside a component called something like cmpProgressBar, give it two custom input properties — CurrentStep (a number) and TotalSteps (a number) — and then drop that single component on every screen. Instead of setting gblStep and copying three controls, you just place the component and set its CurrentStep to gblStep. Change the design once, and all seven update instantly.
2. Stop hard-coding the number 7
The number 7 appears in two places: the width formula (gblStep / 7) and the label ("of 7"). The day you add an eighth screen, you have to hunt down and fix every one of them. Store the total in its own variable instead — set it once in the app’s OnStart:
powerfx
Set(gblTotalSteps, 7)
Then use it everywhere:
powerfx
// Fill width:
recTrack.Width * (gblStep / gblTotalSteps)
// Label:
"Step " & gblStep & " of " & gblTotalSteps
Now adding a screen means changing one number in one place.
3. Let the screens describe their own order
Setting Set(gblStep, 3) by hand on each screen works, but those numbers are a little fragile — reorder your screens and the counting is suddenly wrong. A tidier approach is to define the running order once in OnStart as a collection:
powerfx
ClearCollect(
colSteps,
"NewEventScreen",
"EventDetailsScreen",
"LogisticScreen",
"FoodScreen",
"MaterialScreen",
"ScreenCommunicationSupport",
"OverviewScreen"
)
Then, in each screen’s OnVisible, let the screen look up its own position instead of hard-coding a number:
powerfx
Set(gblStep, CountRows(FirstN(colSteps, LookUp(Sequence(CountRows(colSteps)), true))) )
That formula is a bit much for a first build — a simpler pattern is to keep the manual Set(gblStep, 3) but at least drive gblTotalSteps from CountRows(colSteps). The point is: the fewer magic numbers scattered around, the fewer surprises later.
4. Make it accessible
A progress bar that relies only on colour excludes anyone using a screen reader, and the green-on-amber combination is not the friendliest contrast either. Two small additions help a lot:
- Add a meaningful
AccessibleLabelto the fill or the label, for example"Progress: step " & gblStep & " of " & gblTotalSteps, so assistive technology announces it. - Keep the text label (which we already have) — that alone means the progress isn’t communicated by colour only.
Little things, but they matter for a form that real employees have to fill in.
5. Turn it into a true “stepper”
The bar tells you how far, but not what’s next. A step indicator — a row of seven small numbered circles, with completed ones filled and the current one highlighted — gives users a mental map of the whole journey. You can build this with a horizontal Gallery bound to that same colSteps collection: colour each circle green when its index is less than or equal to gblStep, grey otherwise. Same underlying variable, richer experience.
6. Make it responsive
The track has a fixed Width: 560 and X: 403. That looks great on the screen size you designed for and awkward on anything else. If you wrap the bar in a container and set the track width to something like Parent.Width - 40, the whole thing stretches gracefully across phones, tablets and desktops.
Wrapping up
A progress bar in Power Apps really does come down to one rectangle growing inside another, driven by a single variable and one line of maths: recTrack.Width * (gblStep / gblTotalSteps). Start with the simple copy-paste version to get comfortable with how the pieces fit together, then reach for the reusable component, the gblTotalSteps variable, and the accessibility touches when you are ready to make it production-worthy.
Give your users a sense of where they are in a long form and they will thank you for it — usually by actually finishing the form. Happy building!