Tutorials/Power Apps/The Definitive Guide to Building a Reusable Time Picker Component
Power Appsintermediate

The Definitive Guide to Building a Reusable Time Picker Component

Overcome the standard control library's shortcomings by crafting a custom, native-feeling time selector in a reusable canvas app component.

NA
Narmer Abader
@narmer · Published June 3, 2026

The gap between Power Apps' native control library and the expectations of modern touch-based user interfaces is most obvious when you need to select a specific time. Cascading dropdowns for hours, minutes, and periods technically function, but they introduce unnecessary friction for the user. A custom time picker component solves this elegantly, and building one from scratch teaches you the core concepts of canvas app componentization, state management, and the surprising flexibility of the Power Fx formula language.

This guide walks you through creating a polished, reusable Time Picker component. You will learn how to structure three synchronized galleries, handle the edge cases of a 12-hour clock, and package everything into an asset you can reuse across your entire organization.

The Scenario: Shift Scheduling for a Healthcare Team

Imagine you are building a shift management tool for a hospital ward. Nurses log their exact start and end times. The current solution uses three cascading dropdowns for hour, minute, and AM/PM. It is slow, prone to selection errors, and does not look professional on a mobile device.

Your new component, cmp_TimeWheel, will replace these dropdowns. It must:

  • Accept a DefaultTime input so the current shift can be pre-populated.
  • Expose a SelectedTime output for writing to the data source.
  • Look and feel like the native scroll-and-tap pickers users expect.

Building the Component Shell

Start in the Components pane in Power Apps Studio. Create a new component and name it cmp_TimeWheel.

Set the following base properties:

  • Width: 285
  • Height: 400

Create the input and output properties defined in the requirements.

  1. Input Property: DefaultTime (Type: Time, Default value: Time(9, 0, 0)).
    • Critical: Check the Raise OnReset when value changes checkbox for this property. This ensures the galleries react when the host app modifies the default.
  2. Output Property: SelectedTime (Type: Time). We will bind this to an internal variable later.
  3. Input Properties for Styling: HighlightColor (Type: Color, Default: White), HighlightFill (Type: Color, Default: ColorFade(Color.Blue, 50%)).

Internal State Variable

The component needs a private variable to hold the user's active selection. We will use a uniquely named global variable set via the Set() function. While this technically creates a global variable, scoping it within the component with a distinct prefix keeps it isolated.

Add a Set() call inside the component's OnVisible or simply rely on the galleries' OnSelect to initialize it. The SelectedTime output property will simply hold var_TimeWheel_Selected.

Wiring the Three Galleries

A time picker is essentially three synchronized vertical lists. Add three blank vertical galleries to the component.

Gallery NameWidthTemplateSizeItems Formula
gal_HourParent.Width / 365ForAll(Sequence(12), Text(Value, "[$-en-US]00"))
gal_MinuteParent.Width / 365ForAll(Sequence(60, 0, 1), Text(Value, "[$-en-US]00"))
gal_PeriodParent.Width / 365["AM", "PM"]
powerfxItems property for the Hour gallery
ForAll(Sequence(12), Text(Value, "[$-en-US]00"))
powerfxItems property for the Minute gallery
ForAll(Sequence(60, 0, 1), Text(Value, "[$-en-US]00"))
powerfxItems property for the Period gallery
["AM", "PM"]

Notice the minute gallery uses Sequence(60, 0, 1) which starts at 0, avoiding any off-by-one subtraction logic.

Inside each gallery template, place a single Label control.

  • Label_Hour: X: 0, Y: 0, Width: Parent.TemplateWidth, Height: Parent.TemplateHeight, Text: ThisItem.Value, Align: Center
  • Label_Minute: X: 0, Y: 0, Width: Parent.TemplateWidth, Height: Parent.TemplateHeight, Text: ThisItem.Value, Align: Center
  • Label_Period: X: 0, Y: 0, Width: Parent.TemplateWidth, Height: Parent.TemplateHeight, Text: ThisItem.Value, Align: Center

Styling the Selection State

A good time picker provides strong visual feedback for the selected item. We will use the component's input properties to dynamically style the selected row in each gallery.

For the Color property of each label:

powerfxConditional color based on selection
If(ThisItem.IsSelected, cmp_TimeWheel.HighlightColor, Color.Black)

For the Fill property of each label:

powerfxConditional fill based on selection
If(ThisItem.IsSelected, cmp_TimeWheel.HighlightFill, Transparent)

For hover effects, add a subtle color fade:

powerfxHover fill for a polished interaction
If(!ThisItem.IsSelected, ColorFade(cmp_TimeWheel.HighlightFill, 70%), cmp_TimeWheel.HighlightFill)

Finally, set ShowScrollbar to false for all three galleries. This prevents the scrollbar from appearing on the narrow lists, creating that smooth, wheel-like visual effect.

The Mathematical Magic: OnSelect and Default Values

This is the crux of the component. We need to convert the three gallery selections into a single Time value.

Assembling the Time (OnSelect)

Hold down the Ctrl key and click each gallery in the tree view to multi-select them. Any property change you make will apply to all three simultaneously.

In the OnSelect property, write the following formula. This ensures the time is calculated the moment any gallery item is tapped.

powerfxOnSelect logic for all galleries
Set(
  var_TimeWheel_Selected,
  Time(
      Mod(Value(gal_Hour.Selected.Value), 12)
          + If(gal_Period.Selected.Value = "PM", 12, 0),
      Value(gal_Minute.Selected.Value),
      0
  )
)

Why this works:

  • Mod(Value(gal_Hour.Selected.Value), 12): Converts 12 to 0, and keeps 1–11 as is.
  • + If(Period = "PM", 12, 0): Adds 12 for PM hours (0 becomes 12, 1 becomes 13, etc.).
  • The result perfectly maps to a 24-hour format hour component for the Time() function.

Set the component's output property SelectedTime to var_TimeWheel_Selected.

Defaulting the Galleries

When the component loads (or resets), each gallery must snap to the correct value based on the DefaultTime input property.

Default property of gal_Hour:

The 12 O'Clock Trap

The 12th hour is a special case. Mod(12, 12) equals 0, which converts to text "00". Since our gallery does not have a "00" item (it has "12"), we must catch this case with an explicit If statement.

powerfxDefault selection for the Hour gallery
With(
  {
      wCurrent: Coalesce(var_TimeWheel_Selected, cmp_TimeWheel.DefaultTime)
  },
  LookUp(
      Self.AllItems.Value,
      Value = Text(
          If(Mod(Hour(wCurrent), 12) = 0, 12, Mod(Hour(wCurrent), 12)),
          "[$-en-US]00"
      )
  )
)

Default property of gal_Minute:

powerfxDefault selection for the Minute gallery
With(
  {
      wCurrent: Coalesce(var_TimeWheel_Selected, cmp_TimeWheel.DefaultTime)
  },
  LookUp(
      Self.AllItems.Value,
      Value = Text(Minute(wCurrent), "[$-en-US]00")
  )
)

Default property of gal_Period:

powerfxDefault selection for the Period gallery
With(
  {
      wCurrent: Coalesce(var_TimeWheel_Selected, cmp_TimeWheel.DefaultTime)
  },
  If(Hour(wCurrent) < 12, {Value: "AM"}, {Value: "PM"})
)

Resetting the Component

The OnReset behavior of the component should clear the internal state and force the galleries to re-evaluate their Default properties.

powerfxComponent OnReset behavior
Set(var_TimeWheel_Selected, Blank());
Reset(gal_Hour);
Reset(gal_Minute);
Reset(gal_Period)

Now, if an app screen sets the DefaultTime property of the component instance to Time(14, 30, 0), and then calls Reset(cmp_TimeWheel1), the galleries will correctly snap to 02, 30, PM.

Integrating the Picker into an App

Using the component on a screen is straightforward.

  1. Add a Text input control. Set its Default property to display the selected time.
powerfxDisplaying the time in a text input
Text(cmp_TimeWheel1.SelectedTime, ShortTime)
  1. Add a Button or Icon next to the text input to show or hide the component.
  2. Add an Overlay label behind the component to capture clicks outside and hide the picker.
powerfxToggling the time picker visibility
// OnSelect of the trigger icon/button
Set(varShowPicker, true)

// OnSelect of the overlay label
Set(varShowPicker, false)

// Visible property of cmp_TimeWheel1
varShowPicker

Common Mistakes and How to Fix Them

SymptomLikely CauseFix
Galleries do not snap to the correct default value.Raise OnReset is not checked on the DefaultTime input property, or the Coalesce function is failing because var_TimeWheel_Selected is being set globally outside the component.Check the Raise OnReset box. Ensure var_TimeWheel_Selected is only set inside the component.
12:00 AM displays as 12:00 PM.The Mod / If logic in the Default property of the hour gallery is missing the 12-to-0 conversion.Implement the If(Mod(Hour(wCurrent),12)=0, 12, ...) pattern shown above.
The component is sluggish.Large TemplateSize or animation disabled.Keep TemplateSize under 70. Ensure the gallery Transition property is set to an appropriate animation (e.g., Pop, Push).
The picker looks incomplete or has no border.The component itself does not have a border.Add a Label control at the bottom of the tree view with Fill: Transparent, BorderColor: Color.Black, BorderThickness: 2, Height: Parent.Height, Width: Parent.Width.
I want to use this component in multiple apps.It is currently local to a single app.Publish cmp_TimeWheel to a Component Library. This allows you to import it into any canvas app and maintain a single source of truth for bug fixes and enhancements.

Performance and Delegation Notes

  • Data Sources: The Items properties for the galleries rely entirely on local formulas (Sequence, static arrays). There is no delegation risk here because we are not querying external data sources.
  • LookUp Function: The LookUp function used in the Default properties operates solely on Self.AllItems, which is an in-memory collection built from the local Items formula. This lookup is instantaneous and fully delegable to the extent that it needs to be (it is all local).
  • State Management: Using a single component variable (var_TimeWheel_Selected) for the state is efficient. Avoid creating complex collections or making expensive calls within the OnSelect of the galleries.

Why Build a Reusable Component?

Following the steps above results in more than just a time picker for one screen. It gives you a portable asset:

  1. Consistency: Every time picker in your organization looks and behaves exactly the same.
  2. Maintainability: Fix a bug or enhance the styling in one place, and it propagates to every app using the component library.
  3. Speed: Future apps skip the "build a picker" step and move straight to feature development.

Building this component also deepens your understanding of Power Apps at a platform level—component lifecycles, input/output properties, and the elegant math of time conversion.

Try it out on your next scheduling, booking, or logging app. Your users will appreciate the speed and your teammates will appreciate the reusability.

References