How to: Build responsive layouts
This guide covers techniques for creating layouts that adapt to different window sizes and form factors. You will learn how to use form-factor markup extensions, container queries, breakpoint-driven view models, and reflowing item layouts to build UIs that work across desktop and mobile.
Adaptive grid columns
Use the OnFormFactor markup extension to change your layout structure based on the device type. In the following example, a two-column grid with a sidebar appears on desktop while mobile users see a single-column layout:
<Grid ColumnDefinitions="{OnFormFactor Desktop='250,*', Mobile='*'}">
<Border Grid.Column="0" Background="#F3F4F6"
IsVisible="{OnFormFactor Desktop=True, Mobile=False}">
<!-- Sidebar: visible on desktop, hidden on mobile -->
<ListBox ItemsSource="{Binding MenuItems}" />
</Border>
<ContentControl Grid.Column="{OnFormFactor Desktop=1, Mobile=0}"
Content="{Binding CurrentPage}" />
</Grid>
OnFormFactor resolves at startup, so the value does not change if you resize the window at runtime. If you need your layout to respond to live size changes, use container queries or a breakpoint-based approach instead.
Container queries
Container queries adapt layout based on a control's own rendered size rather than the window size. This makes them ideal for reusable components that may appear in panels of varying width.
The following example switches a StackPanel between vertical and horizontal orientation depending on the width of its parent Border:
<Border>
<Border.Styles>
<!-- Vertical layout when container is narrow -->
<Style Selector="Border[Width<400] > StackPanel">
<Setter Property="Orientation" Value="Vertical" />
</Style>
<!-- Horizontal layout when container is wide -->
<Style Selector="Border[Width>=400] > StackPanel">
<Setter Property="Orientation" Value="Horizontal" />
</Style>
</Border.Styles>
<StackPanel Spacing="8">
<TextBlock Text="Label" />
<TextBox Text="{Binding Value}" />
</StackPanel>
</Border>
See Container queries for the full syntax and named-container support.
Breakpoint-based layout
When you need fine-grained control over layout transitions, you can implement breakpoints by observing the window width in your view model. Define boolean properties for each breakpoint tier, then bind your XAML to them:
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private bool _isCompact;
[ObservableProperty]
private bool _isWide;
public void UpdateLayout(double windowWidth)
{
IsCompact = windowWidth < 640;
IsWide = windowWidth >= 1024;
}
}
Call UpdateLayout from the window's OnSizeChanged override so that your properties stay in sync as the user resizes:
// In MainWindow code-behind
protected override void OnSizeChanged(SizeChangedEventArgs e)
{
base.OnSizeChanged(e);
if (DataContext is MainViewModel vm)
vm.UpdateLayout(e.NewSize.Width);
}
In your AXAML, swap between compact and wide views by binding IsVisible to the breakpoint properties:
<Grid>
<!-- Compact layout: single column -->
<StackPanel IsVisible="{Binding IsCompact}" Spacing="8">
<views:SidebarView />
<views:ContentView />
</StackPanel>
<!-- Wide layout: two columns -->
<Grid IsVisible="{Binding !IsCompact}" ColumnDefinitions="280,*">
<views:SidebarView Grid.Column="0" />
<views:ContentView Grid.Column="1" />
</Grid>
</Grid>
This approach gives you full programmatic control and works well when your layout logic involves more than simple width thresholds (for example, combining orientation and platform checks).
Use SplitView for a collapsible sidebar
The SplitView control provides a built-in collapsible pane pattern. Set DisplayMode to CompactInline so the pane collapses to a narrow strip showing icons, then expands to reveal labels when you toggle IsPaneOpen:
<SplitView IsPaneOpen="{Binding IsSidebarOpen}"
DisplayMode="CompactInline"
CompactPaneLength="48"
OpenPaneLength="250">
<SplitView.Pane>
<StackPanel>
<Button Content="☰" Command="{Binding ToggleSidebarCommand}"
HorizontalAlignment="Left" Width="48" />
<ListBox ItemsSource="{Binding MenuItems}"
SelectedItem="{Binding SelectedMenuItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Spacing="12">
<PathIcon Data="{Binding Icon}" Width="16" />
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</SplitView.Pane>
<SplitView.Content>
<ContentControl Content="{Binding CurrentPage}" />
</SplitView.Content>
</SplitView>
You can bind IsPaneOpen to your breakpoint properties so the sidebar opens automatically on wide screens and collapses on narrow ones.
Responsive card grid
Use ItemsControl with a WrapPanel as the display panel to create a card grid that reflows as the available width changes. See Custom panel for guidance on how to customize the ItemsPanel in an ItemsControl.
- Preview
- MainWindow.axaml
- MainViewModel.cs
- Models/CardItem.cs

<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ResponsiveCardGrid.ViewModels"
xmlns:models="using:ResponsiveCardGrid.Models"
x:Class="ResponsiveCardGrid.Views.MainWindow"
x:DataType="vm:MainViewModel">
<ItemsControl ItemsSource="{Binding Cards}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="models:CardItem">
<Border Background="White"
CornerRadius="8"
Padding="16"
BorderBrush="#E5E7EB"
BorderThickness="1">
<StackPanel Spacing="8">
<TextBlock Text="{Binding Title}"
FontWeight="Bold"
Foreground="Black" />
<TextBlock Text="{Binding Description}"
TextWrapping="Wrap"
Foreground="Gray" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
using System.Collections.ObjectModel;
using ResponsiveCardGrid.Models;
namespace ResponsiveCardGrid.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
public ObservableCollection<CardItem> Cards { get; } =
[
new() { Title = "Inbox", Description = "Messages waiting for a reply from the team." },
new() { Title = "Drafts", Description = "Unfinished notes you started but never sent." },
new() { Title = "Scheduled", Description = "Items queued to go out later this week." },
new() { Title = "Archive", Description = "Everything you have filed away for later reference." },
new() { Title = "Starred", Description = "Cards you flagged as worth a second look." },
new() { Title = "Trash", Description = "Deleted items, kept for thirty days before removal." }
];
}
using CommunityToolkit.Mvvm.ComponentModel;
namespace ResponsiveCardGrid.Models;
public partial class CardItem : ObservableObject
{
[ObservableProperty]
public partial string Title { get; set; } = string.Empty;
[ObservableProperty]
public partial string Description { get; set; } = string.Empty;
}
Platform-specific spacing
Use OnFormFactor to adjust spacing, margins, and font sizes per platform. Mobile interfaces typically benefit from larger touch targets and slightly larger text:
<StackPanel Spacing="{OnFormFactor Desktop=8, Mobile=12}"
Margin="{OnFormFactor Desktop='16', Mobile='8'}">
<TextBlock Text="Content" FontSize="{OnFormFactor Desktop=14, Mobile=16}" />
</StackPanel>
Adaptive font sizes
Use container queries to scale text based on the size of an ancestor. Declare a container on the parent element with Container.Name and Container.Sizing, then apply a ContainerQuery to set different font sizes at different widths:
<Panel Container.Name="content" Container.Sizing="Width">
<Panel.Styles>
<Style Selector="TextBlock.title">
<Setter Property="FontSize" Value="24" />
</Style>
<!-- Smaller title when the container is narrow -->
<ContainerQuery Name="content" Query="max-width:500">
<Style Selector="TextBlock.title">
<Setter Property="FontSize" Value="18" />
</Style>
</ContainerQuery>
</Panel.Styles>
<TextBlock Classes="title" Text="Responsive heading" />
</Panel>
This technique keeps your typography responsive without relying on window-level breakpoints, so the text adapts correctly even when your control is hosted inside a split pane or dialog.
See also
- Container queries: Responsive styling based on container size.
- Layout: Avalonia layout system overview.
- Grid how-to: Grid layout patterns.
- Cross-platform architecture: Platform detection and branching.