MAUI 9: Microsoft Cross Platform Guide
HOW TO GUIDES Feb. 8, 2026, 5:30 p.m.

MAUI 9: Microsoft Cross Platform Guide

Microsoft’s .NET Multi‑Platform App UI (MAUI) has hit version 9, bringing a fresh set of tools that make building native apps for iOS, Android, macOS, and Windows smoother than ever. Whether you’re a seasoned Xamarin developer or just starting out, MAUI 9 streamlines project setup, introduces modern UI patterns, and tightens the integration with Visual Studio. In this guide we’ll walk through the core concepts, show you how to get a cross‑platform app up and running, and share pro tips that will help you avoid common pitfalls.

Why MAUI 9 Matters

MAUI 9 is more than just a version bump; it’s a strategic shift toward a unified development experience. The new single‑project structure collapses platform‑specific folders into one logical view, reducing clutter and simplifying build pipelines. Additionally, the revamped Hot Reload and Live Preview features let you see UI changes instantly, cutting feedback loops dramatically.

From a performance standpoint, MAUI 9 ships with the .NET 8 runtime, which includes tiered compilation and improved garbage collection. This translates to faster start‑up times and smoother animations on low‑end devices—crucial for retaining users in today’s competitive app market.

Key New Features

  • Unified single‑project layout with Platforms folder for all targets.
  • Enhanced Graphics API for drawing custom controls using the new Microsoft.Maui.Graphics namespace.
  • Native Blazor Hybrid support, letting you embed Razor components directly inside MAUI pages.
  • Improved Shell Navigation with route‑based parameters and deep linking out of the box.
Pro tip: Turn on EnablePreviewers in your .csproj to get live UI previews while you code. This tiny setting saves hours of manual testing on emulators.

Setting Up Your Development Environment

Before diving into code, make sure you have the right tools. MAUI 9 requires Visual Studio 2022 17.9 or later on Windows, and Visual Studio for Mac 2022 8.10+ on macOS. Install the ".NET Multi‑Platform App UI development" workload, which pulls in Android SDKs, iOS simulators, and the necessary .NET SDKs.

  1. Download and install the latest Visual Studio.
  2. During installation, select .NET Multi‑Platform App UI development under Workloads.
  3. After installation, open the Visual Studio Installer again and ensure the Android SDK and iOS tooling are up to date.

Once Visual Studio is ready, verify the MAUI templates by opening a terminal and running:

dotnet new --install Microsoft.Maui.Templates::9.0.0

If the command completes without errors, you’re good to go. The next step is to create a fresh project.

Creating Your First MAUI 9 App

Open Visual Studio, choose “Create a new project,” and select “.NET MAUI App (Preview).” Name it TravelBuddy—a simple travel itinerary manager that works on all platforms.

Visual Studio will generate a single‑project solution with the following structure:

  • App.xaml & App.xaml.cs – global resources and application lifecycle.
  • MainPage.xaml & MainPage.xaml.cs – the default UI.
  • Platforms/ – subfolders for Android, iOS, macOS, and Windows.
  • Resources/ – fonts, images, and raw assets shared across platforms.

Let’s replace the default UI with a practical layout that lists upcoming trips and allows users to add new entries.

Designing the UI with XAML

Open MainPage.xaml and replace its content with the following markup. Notice the use of CollectionView for efficient list rendering and Shell navigation for a clean back‑stack.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TravelBuddy.MainPage"
             Title="My Trips">

    <StackLayout Padding="20">
        <Label Text="Upcoming Trips"
               FontSize="24"
               FontAttributes="Bold"
               HorizontalOptions="Center" />

        <CollectionView ItemsSource="{Binding Trips}"
                        SelectionMode="Single"
                        SelectedItem="{Binding SelectedTrip}"
                        HeightRequest="300">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <Grid Padding="10" ColumnDefinitions="*,Auto">
                        <Label Text="{Binding Destination}"
                               FontSize="18"
                               VerticalOptions="Center" />
                        <Label Text="{Binding Date, StringFormat='{0:MMM d}'}"
                               FontSize="14"
                               TextColor="Gray"
                               Grid.Column="1"
                               VerticalOptions="Center" />
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>

        <Button Text="Add Trip"
                Command="{Binding AddTripCommand}"
                BackgroundColor="#0066CC"
                TextColor="White"
                CornerRadius="8"
                Margin="0,20,0,0" />
    </StackLayout>
</ContentPage>

This UI binds to a view model that we’ll create next. The CollectionView automatically adapts to the device’s screen size, providing a native scrolling experience on both phones and desktops.

Implementing the ViewModel

In the ViewModels folder, add a new class called TripViewModel.cs. This class uses ObservableCollection to keep the UI in sync with the underlying data.

using System.Collections.ObjectModel;
using System.Windows.Input;
using Microsoft.Maui.Controls;

namespace TravelBuddy.ViewModels
{
    public class TripViewModel : BindableObject
    {
        public ObservableCollection<Trip> Trips { get; } = new();

        private Trip _selectedTrip;
        public Trip SelectedTrip
        {
            get => _selectedTrip;
            set
            {
                _selectedTrip = value;
                OnPropertyChanged();
                // Optional: navigate to details page
            }
        }

        public ICommand AddTripCommand { get; }

        public TripViewModel()
        {
            // Seed with sample data
            Trips.Add(new Trip { Destination = "Paris", Date = DateTime.Now.AddDays(10) });
            Trips.Add(new Trip { Destination = "Tokyo", Date = DateTime.Now.AddMonths(1) });

            AddTripCommand = new Command(OnAddTrip);
        }

        private async void OnAddTrip()
        {
            // Simple prompt for demo purposes
            string destination = await Application.Current.MainPage.DisplayPromptAsync(
                "New Trip", "Enter destination:");

            if (!string.IsNullOrWhiteSpace(destination))
            {
                Trips.Add(new Trip
                {
                    Destination = destination,
                    Date = DateTime.Now.AddDays(7) // default to one week ahead
                });
            }
        }
    }

    public class Trip
    {
        public string Destination { get; set; }
        public DateTime Date { get; set; }
    }
}

Notice the use of BindableObject and OnPropertyChanged—the backbone of MAUI’s data binding system. The AddTripCommand demonstrates a simple modal prompt that works consistently on Android, iOS, and Windows.

Hooking Up the ViewModel

Open MainPage.xaml.cs and set the BindingContext to an instance of TripViewModel. This single line wires up the entire UI.

using TravelBuddy.ViewModels;

namespace TravelBuddy;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        BindingContext = new TripViewModel();
    }
}

Run the app on an Android emulator, an iOS simulator, or Windows. You should see a list of trips and be able to add new ones with a native prompt. This simple example already showcases MAUI’s cross‑platform consistency.

Deep Diving: Platform‑Specific Customizations

While MAUI abstracts most platform differences, real‑world apps often need tweaks that only make sense on a particular OS. MAUI 9 introduces the #if ANDROID, #if IOS, and similar directives that you can use directly in XAML or C# code.

Example: Using Native Map Control on iOS and Android

Suppose you want to display a map preview for each trip. Both iOS (MapKit) and Android (Google Maps) expose native map views, but MAUI’s Map control abstracts them. To add a platform‑specific pin image, you can do the following:

using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Controls.PlatformConfiguration;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;

public class TripMap : ContentView
{
    public TripMap()
    {
        var map = new Map
        {
            IsShowingUser = true,
            HeightRequest = 200
        };

#if ANDROID
        map.PinColor = Colors.Red;
#elif IOS
        map.PinColor = Colors.Blue;
#endif

        Content = map;
    }
}

Now the map will show red pins on Android devices and blue pins on iOS, while the rest of the UI stays unchanged. This pattern scales well for things like status bar styling, custom renderers, or invoking platform‑specific APIs.

Pro tip: Keep platform‑specific code in separate partial classes. This keeps the shared logic clean and makes future migrations easier.

Integrating Blazor Hybrid

MAUI 9 makes it straightforward to embed a Blazor component inside a native page. This is handy when you already have a web dashboard and want to reuse it on mobile.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:blazor="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui"
             x:Class="TravelBuddy.DashboardPage">

    <blazor:BlazorWebView HostPage="wwwroot/index.html">
        <blazor:BlazorWebView.RootComponents>
            <blazor:RootComponent Selector="#app"
                                 ComponentType="{x:Type local:Dashboard}" />
        </blazor:BlazorWebView.RootComponents>
    </blazor:BlazorWebView>
</ContentPage>

Place your Razor component (Dashboard.razor) in the shared project, and it will render natively on every platform. The performance is comparable to a pure native view because the WebView runs in a lightweight process.

Testing, Debugging, and Performance Optimizations

MAUI 9’s tooling makes it easier to catch UI glitches early. The new Live Visual Tree lets you inspect the XAML hierarchy at runtime, while Hot Reload updates XAML without rebuilding the app. Use the dotnet build -c Release command to generate an optimized binary before publishing.

Profiling with .NET MAUI Diagnostics

Open the Diagnostic Tools window in Visual Studio while the app runs. Look for the Memory Usage and CPU Usage tabs. If you notice spikes when scrolling the CollectionView, consider enabling RecycleElement mode:

<CollectionView ItemsSource="{Binding Trips}"
                SelectionMode="Single"
                SelectedItem="{Binding SelectedTrip}"
                HeightRequest="300"
                ItemsLayout="VerticalList"
                ItemSizingStrategy="MeasureAllItems">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Vertical"
                           ItemSpacing="5"
                           SnapPointsAlignment="Start" />
    </CollectionView.ItemsLayout>
    <CollectionView.ItemTemplate>
        ...
    </CollectionView.ItemTemplate>
</CollectionView>

Setting ItemSizingStrategy="MeasureAllItems" reduces layout passes, especially on low‑end Android devices.

Handling Platform Permissions

Many real‑world apps need runtime permissions—for example, location access for the map feature. MAUI 9 provides a unified Permissions API.

using Microsoft.Maui.ApplicationModel;

public async Task<bool> RequestLocationPermissionAsync()
{
    var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
    if (status != PermissionStatus.Granted)
    {
        status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
    }
    return status == PermissionStatus.Granted;
}

Call this method before initializing the map. The API automatically shows the appropriate native dialog on each platform.

Pro tip: Cache permission results in a local variable during the session to avoid repeatedly prompting the user.

Deploying and Publishing

When you’re ready to ship, MAUI 9 simplifies the CI/CD pipeline with dotnet maui commands. For Android, generate an AAB (Android App Bundle) with:

dotnet publish -f:net8.0-android -c Release /p:AndroidPackageFormat=aab

For iOS, use the following to produce an IPA for the App Store:

dotnet publish -f:net8.0-ios -c Release /p:RuntimeIdentifier=ios-arm64

Both commands produce a publish folder containing the signed package. Upload the artifacts to Azure DevOps, GitHub Actions, or any CI platform that supports .NET builds. Remember to set up code signing certificates and provisioning profiles for iOS, and a keystore for Android.

App Store Optimization (ASO) Checklist

  • Provide high‑resolution screenshots for each device family.
  • Write a concise, keyword‑rich description.
  • Localize strings using .resx files; MAUI automatically picks the right language.
  • Test the app on the latest OS versions (Android 14, iOS 17, Windows 11).

Real‑World Use Cases for MAUI 9

Enterprise field service apps: Companies can build a single codebase that runs on technicians’ tablets (Android) and managers’ desktops (Windows). The built‑in Map control, offline storage via SQLite, and secure authentication using Microsoft Identity make MAUI a solid choice.

Consumer health trackers: MAUI’s low‑level graphics API lets you draw smooth charts for heart‑rate data, while the BluetoothLE library integrates with wearable devices. Because the UI is shared, you can launch updates to both Android and iOS simultaneously.

Education platforms: The Blazor Hybrid feature enables you to embed existing web‑based lessons inside a native shell, giving students a seamless experience without rewriting content.

Best Practices and Common Pitfalls

Even with MAUI’s abstractions, certain patterns can trip up developers:

Share this article