Building a Two-Level Menu Component in Power Apps

Building a two-level menu in canvas apps is easy; just use a gallery for the parent options then a nested gallery for the related child options. Bingo.

That does work of course, but there’s not a lot of awareness around the potential performance impact of using nested galleries. This is especially true for a client development I’ve been involved during the past year. Huge app, loads of screens, lots of functionality and controls. Extracting the most miniscule amount of performance has been absolutely critical, so I worked with the team to design a two-level menu with just a single gallery.

This article will show the logic and build steps so you can do the same. In my examples, I’ll be using the very-real business scenario of Star Wars related data.

First off, I have a large app with lots of screens. There’s screens for the parent options (boxed in pink) as well as related child items (boxed green):

We’ll need to define this relationship/hierarchy in the data that feeds our menu component. Speaking of which, have you ever heard or read anyone say things like “it’s all about your data“, “it starts with your data“, etc? That’s where we start with this method. We’ll build a collection to house our data, using integer values to drive specific gallery behaviour. Here is an extract of the table structure we’re using for the collection:

The logic for each column is explained below:

MasterNav: This is the parent reference. Note in the image above, both entries have the same MasterNav entry. This is so we can determine what child entry relates to what parent category, as such we know the entry for Leia Organa relates to the Rebel Alliance category.
MasterNavID:
This is a 1 for any parent entries, 0 for any child entries.
SubNav:
The child entry option where required.
SubNavID:
This is a unique ID per entry so the collection can be sorted in the specific order.
NavScreen:
The screen that will be shown to the user when the menu option is clicked.
Icon:
In this example, only parent options have an icon. You could extend this to all options, change it to SVG’s, use out-of-the-box icons – it’s your call. I’ve opted for emoji’s.

Here is an extract of the collection so you can see the structure that’s going to drive the functionality. Note the parent rows with their MasterNavID of 1 (boxed pink) with their respective child options listed underneath:

The component

With the data in place, we can build our menu component. I’ve created a new solution, then a new canvas app within that solution. With the app in Edit mode, select Components, then New component:

I’ve renamed my component to cmpNavigationMenu, set the Width to 300 and Height to 640. As this is an in-app component, I’ll also switch Access app scope to On so we can inject the collection built in App OnStart:

Add a Blank Vertical Gallery to the component. Set the Data source to the collection/data table that houses the menu logic, then update the following gallery properties:

Property Value
Height
Parent.Height
Width
Parent.Width
Template Size
40
Template Padding
1

I’ve got my icons stored as an emoji, which is text and therefore needs to be shown in my gallery via a Label control. If you’re using SVG’s you’ll need an Image control here, or an Icon if using out-of-the-box icons.

The label in my gallery has the following properties:

Property Value
Height
40
Width
40
X position
Y position
Text
ThisItem.Icon
Text alignment
Align centre

Next, add a button to the gallery. Update the button properties as per the table below:

Property Value
Border style
BorderStyle.None
Fill
Color.Transparent
FocusedBorderColor
Self.Fill
FontSize
14
HoverColor
Self.Color
HoverFill
Self.Fill
PressedColor
Self.Color
PressedFill
Self.Fill
Text
ThisItem.SubNav
Text alignment
Align left
Height
40
Width
250
Position X
40
Position Y

In good shape so far, the base structure & controls are in place:

We need to fine tune to make it look like and do what we want.

OnSelect action

When the user clicks on a menu option, they’ll need to navigate to the selected screen. To drive some conditional formatting, we can also set a variable at the same time. You can only set global variables inside components.

With the button selected, add the following Power Fx into the OnSelect property:

				
					// Navigate to the selected screen
Navigate(ThisItem.NavScreen);
// Set the internal component variable, to drive Filter logic for gllNavMenu
Set(
    gvSelectedNavItem,
    ThisItem.MasterNav
)
				
			

These two actions are separated and not inside a Concurrent function, as Navigate functions can’t be embedded within concurrent.

Formatting tweaks

We are going to indent the button text for child items, to give the appearance that they’re nested under their parent. With the button selected, add the following Power Fx into the PaddingLeft property:

				
					// Indent text if child option, to give appearance of nested/sub options
If(
    ThisItem.MasterNavID = 1,0,15)
				
			

Add the following Power Fx into the FontWeight property:

				
					If(
    // If the SubNav equals the variable defined from OnSelect,
    // or the current screen equals the selected screen from the menu
    Or(
        ThisItem.SubNav = gvSelectedNavItem,
        ThisItem.NavScreen = App.ActiveScreen
    ),
    FontWeight.Bold,
    FontWeight.Normal
)
				
			

Add the following Power Fx into the Color property:

				
					If(
    Self.FontWeight = FontWeight.Bold,
    ColorValue("#1F3152"),
    ColorValue("#797979")
)
				
			

Filter logic

Finally, we need to update the Items property of the gallery. It will use the variable set in the OnSelect property of the button, as well as the MasterNav column in the data structure to perform the logic.

With the gallery selected in the component, update the Items property to the following Power Fx:

				
					Filter(
    // Sort by the unique ID
    SortByColumns(
        colNavMenu,
        "SubNavID",
        SortOrder.Ascending
    ),
    // x2 Filters
    // First is to always show the parent options, MasterNav=1
    MasterNavID = 1 || 
    // Second is to drive which child items are visible depending on the MasterNav of the selected option
    If(
        !IsBlank(gvSelectedNavItem),
        MasterNav = gvSelectedNavItem,
        NavScreen = App.ActiveScreen
    )
)
				
			

The component should end up looking like this in the Components design view:

That’s fine; the component needs the variable to be set to drive the logic and conditional formatting.

Final product

With the component added to each screen, we can see it working in all its glory:

Want the solution?

I’ve added an unmanaged solution to my GitHub, which contains a canvas app with the component build. You can download the solution from this repo and import the solution into your chosen environment. If GitHub isn’t your thing, you can also also find the solution in the Power Apps Community App Samples area.

Do you have any cool menu-building techniques or tips in canvas apps? Let me know in the comments below! If you liked this article and want more epic Power Platform stuff to land in your inbox every week, don’t forget to subscribe 😊

What do you think?

Your email address will not be published. Required fields are marked *

7 Comments
  • Hunter White
    September 18, 2023

    Amazing Solution! I learned a ton from this blog post! Thanks for sharing! Keep up the great work!

    • Craig White
      September 18, 2023

      Thanks Hunter, always appreciate your feedback!

  • Li
    September 18, 2023

    Great solution! Trying to upload the zip file, unable to. It gives me error message says something is wrong. I tried different environment too.

    • Craig White
      September 18, 2023

      Hi Li, are you following the typical ‘import solution’ instructions as per MS Docs:
      https://learn.microsoft.com/en-us/power-apps/maker/data-platform/import-update-export-solutions

      If you are and it’s not working, please let me know a bit more about the error if you can, or what type of environment you’re in and level of access. There’s nothing in the solution other than a .msapp file, maybe try importing it into a Developer Plan environment:
      https://powerapps.microsoft.com/en-us/developerplan/

      • Anonymous
        September 18, 2023

        Hi Graig, Yes, I did follow the typical import solution instruction as I always have. I did in default and sandbox environment. Well, I will just follow the instructions in your article. Thank you so much for the quick reply and it’s exactly what I need for my new project!

        • Craig White
          September 18, 2023

          That’s very odd, maybe try it in a Developer env and see what happens. Is it your own tenant or a work one?
          If you need any help with following along, let me know. Good luck!

          • Anonymous
            September 18, 2023

            It’s my bad, I didn’t read carefully. I didn’t import it in solution. Now it’s imported without issues. Thanks again!