XAML & Layouts
Here's the mental model to carry through this whole phase: a page is one root layout holding controls. That's it. A screen in MAUI is a ContentPage, the ContentPage holds exactly one thing — a layout — and the layout's whole job is to arrange the controls you put inside it. Controls are the leaves: labels, buttons, text boxes. Layouts are the branches that decide where those leaves sit.
The markup you write to describe this tree is XAML — an XML dialect. And the part that trips people up at first is worth saying out loud now: XAML isn't a separate magic language. Every XAML element is just a C# object. When you write <Label Text="Hi" />, MAUI creates a Label object and sets its Text property to "Hi". The page's <x:Class> attribute literally names a C# partial class, and the XAML becomes part of that class at build time. So XAML describes the UI, and that description compiles down to the same C# objects you could have written by hand.
📝 You can build the entire UI in C# instead of XAML —
new ContentPage { Content = new Label { Text = "Hi" } }. Most MAUI code uses XAML because a declarative tree is easier to read and tweak than nested constructor calls. We'll use XAML throughout, but remember it's objects underneath.
The page and its one root layout
A bare page looks like this:
What just happened: The xmlns lines map XML namespaces to MAUI's control library and to XAML's own keywords (that's where x:Class comes from). x:Class="NotesApp.MainPage" ties this file to the C# partial class MainPage in your project. And the ContentPage holds a single child — here a lone Label. A ContentPage has exactly one Content. To show more than one control, that single child has to be a layout that holds the rest.
The layouts you'll actually use
Stack layouts — children in a line
VerticalStackLayout arranges its children top to bottom; HorizontalStackLayout does it left to right. They're the simplest containers and great for short, linear groups.
What just happened: Three controls stacked vertically with a 12-unit gap between each (Spacing) and 20 units of breathing room around the whole group (Padding). Padding is space inside the container's edge; Spacing is the gap between children. Note the page now has one root child (the stack), and the stack holds the three controls — the mental model in action.
Grid — rows and columns, the real workhorse
Stacks are fine for a line of controls, but real screens have structure: a header pinned at the top, a body that fills the rest, a footer that hugs the bottom. That's a Grid.
A Grid defines RowDefinitions and ColumnDefinitions, then each child says which cell it lives in with Grid.Row and Grid.Column (both default to 0).
What just happened: RowDefinitions="Auto,*,Auto" makes three rows. Auto sizes a row to exactly fit its content — the title and the button take only the height they need. * means "take all the leftover space" — the middle row stretches to fill whatever's left, which is exactly what you want for a scrolling list of notes. The header sits in row 0, the body in row 1, the button in row 2. This is the notes-list skeleton: header on top, body fills the gap, action button parked at the bottom.
💡 The sizing tokens are the whole point of
Grid.Auto= size to content.*= take the remaining space (and2*takes twice the share of a plain*, so*,2*splits leftover space one-third / two-thirds). Because*rows and columns flex with the screen, the same layout looks right on a narrow phone and a wide desktop — no per-device code.
ScrollView — when content overflows
A ContentView or stack won't scroll on its own; if its content is taller than the screen, the overflow is clipped. Wrap it in a ScrollView and it scrolls.
<!-- ...many more controls... -->
What just happened: ScrollView takes one child (here the stack) and gives it a scrollable viewport. Anything taller than the screen now scrolls instead of getting cut off. (FlexLayout and AbsoluteLayout also exist for wrap-and-flow and pixel-precise positioning, but you'll reach for them far less often — start with stacks, Grid, and ScrollView.)
Common controls and their key properties
Layouts arrange things; controls are the things. The handful you'll use constantly:
Label— displays text.Button— a tappable button with aClickedevent.Entry— single-line text input.Editor— multi-line text input (think note body).Image— shows an image from aSource.
Properties are written as XAML attributes. The ones you'll set most often:
What just happened: FontSize and TextColor style the label. HorizontalOptions (and its sibling VerticalOptions) control how a child sits in the space its parent gives it — the values are Start, Center, End, and Fill; here the title is centered and the button is pushed to the right (End). Margin="0,16,0,0" adds space outside the button (left, top, right, bottom order), nudging it down from the editor. Placeholder is the grey hint text shown in an empty Entry/Editor.
⚠️ Margin vs. Padding catches everyone once: Padding is space inside a container, before its children start. Margin is space outside a control, pushing its neighbors away. Same four-number order —
left,top,right,bottom— but opposite sides of the control's edge.
One real warning: don't nest stacks endlessly
When stacks are the only tool you know, it's tempting to build a whole screen by burying stacks inside stacks inside stacks to push things around. Resist it.
⚠️ Deeply nested stack layouts hurt performance and are miserable to maintain. Each stack measures and arranges its children, and nesting multiplies that work on every layout pass — on a list that scrolls, it shows up as jank. Worse, six levels deep, nobody can tell which container owns which spacing. A Grid with a few RowDefinitions/ColumnDefinitions does the same job flat: one container, every child placed by row and column, far less measuring. For anything beyond a short line of controls, reach for Grid first.
Building the notes app's main page
Let's put it together into the real screen for our running notes app: a title at the top, a list area in the middle (a placeholder for now — the real list comes in Phase 3), and an Add button at the bottom.
<!-- Header -->
<!-- List area (placeholder until Phase 3) -->
<!-- Action -->
What just happened: One ContentPage, one root Grid, three children — exactly the mental model. The Auto,*,Auto rows give us a fixed-height header, a flexible body that fills the screen (so when real notes arrive they have room to grow and scroll), and a fixed-height button at the bottom. The body label is just a placeholder; in Phase 3 we swap it for a CollectionView bound to actual data. HorizontalOptions="Fill" on the button makes it stretch the full width. Run this and you've got the skeleton of the whole app — and you built it with one flat Grid instead of a tangle of stacks.
Recap
- A page is one root layout holding controls —
ContentPagehas exactly oneContent, and to show several controls that one child must be a layout. - XAML is C# objects. Each element constructs an object and each attribute sets a property;
x:Classties the file to a C# partial class. - Stack layouts line children up (
Spacingbetween,Paddingaround);Gridplaces children in rows/columns usingGrid.Row/Grid.Column, withAuto= size-to-content and*= take remaining space;ScrollViewlets overflow scroll. - Common controls —
Label,Button,Entry,Editor,Image— are styled with attributes likeFontSize,TextColor,HorizontalOptions,Margin, andPadding. - Prefer
Gridover deeply nested stacks for any non-trivial screen — it's faster and far more maintainable — and lean on*sizing so one layout works across phone, tablet, and desktop.
Quick check
[
{
"q": "In a Grid with RowDefinitions=\"Auto,*,Auto\", what does the middle \"*\" row do?",
"choices": ["Sizes itself to exactly fit its content", "Takes up all the leftover space after the Auto rows", "Is hidden until content is added", "Forces a fixed height of 1 unit"],
"answer": 1,
"explain": "\"*\" means take the remaining space. Combined with Auto rows above and below, it gives a flexible body between a fixed header and footer."
},
{
"q": "How many direct children can a ContentPage's Content hold?",
"choices": ["As many as you like", "Exactly one (usually a layout)", "Up to ten", "Only controls, never layouts"],
"answer": 1,
"explain": "A ContentPage holds one Content. To show multiple controls, that single child must be a layout that holds the rest."
},
{
"q": "Why prefer a Grid over deeply nested stack layouts for a non-trivial screen?",
"choices": ["Grids can't be styled, so they're simpler", "Nested stacks hurt performance and maintainability; a flat Grid does the same job", "Stacks don't work on iOS", "Grids are the only layout that supports Padding"],
"answer": 1,
"explain": "Each nested stack adds measure/arrange work and obscures which container owns what. A Grid places everything flat by row and column — faster and clearer."
}
]
← Phase 1: What MAUI Is & Your First App · Guide overview · Phase 3: Controls & Data Binding →
Check your understanding
1. In a Grid with RowDefinitions="Auto,*,Auto", what does the middle "*" row do?
2. How many direct children can a ContentPage's Content hold?
3. Why prefer a Grid over deeply nested stack layouts for a non-trivial screen?