Skip to main content

.NET MAUI Tutorial 2026: Build Cross-Platform Apps in C#

Learn .NET MAUI in 2026 to build iOS, Android, Windows & Mac apps with one C# codebase. Step-by-step tutorial with code examples. Start building today!

If you want to build native iOS, Android, Windows, and macOS apps from a single C# codebase, .NET MAUI is the framework you should learn in 2026. .NET MAUI (Multi-platform App UI) is Microsoft's modern evolution of Xamarin.Forms, and with the .NET 10 release it has matured into a fast, stable, production-ready toolkit. In this .NET MAUI tutorial, you'll learn what MAUI is, how it works, how to build your first cross-platform app, and the best practices that separate hobby projects from shipping products.

Whether you're a beginner asking "how do I build a mobile app in C#?", an intermediate developer comparing MAUI vs Xamarin, or a senior engineer evaluating cross-platform app development for your team, this guide covers it end to end with runnable code.

What Is .NET MAUI and Why Use It in 2026?

.NET MAUI is an open-source, cross-platform framework for building native desktop and mobile apps with C# and XAML. Write your UI and business logic once, and MAUI compiles it to truly native applications on four platforms:

  • Android (API 21+)
  • iOS (15+)
  • macOS (via Mac Catalyst)
  • Windows (via WinUI 3)

The key reason developers choose .NET MAUI for cross-platform app development is code sharing without sacrificing native performance. Unlike web-based frameworks that ship a browser engine, MAUI renders genuine native controls. A Button in MAUI becomes a UIButton on iOS and an android.widget.Button on Android. Your users get a native look, feel, and speed; you maintain one project.

Here's why .NET MAUI is a strong choice in 2026:

  • Single project structure — one .csproj targets all platforms, with shared resources, fonts, and images.
  • Native AOT and performance gains — faster startup and lower memory usage thanks to ongoing .NET runtime improvements.
  • Hot Reload — change XAML or C# and see results instantly without rebuilding.
  • Massive C# ecosystem — reuse NuGet packages, dependency injection, and your existing .NET skills.
  • Blazor Hybrid support — mix web UI (Razor components) inside native apps when it suits you.

.NET MAUI vs Xamarin: What Changed?

If you've used Xamarin.Forms, .NET MAUI will feel familiar but meaningfully better. Microsoft ended Xamarin support in May 2024, so MAUI is now the official path forward. The biggest differences:

  • Single project instead of separate head projects per platform.
  • Unified resource handling — drop one image and MAUI generates the correct resolutions per device.
  • Handler architecture replaces the old slow "renderers," giving better performance and easier customization.
  • Built on modern .NET rather than the legacy Mono stack.

For new projects, there is no reason to start with Xamarin. For existing Xamarin.Forms apps, Microsoft provides a .NET Upgrade Assistant to migrate to MAUI.

How to Set Up Your .NET MAUI Development Environment

Before writing code, install the tooling. On Windows, use Visual Studio 2022 (17.12+) and select the .NET Multi-platform App UI development workload. On macOS, use VS Code with the .NET MAUI extension (the standalone Visual Studio for Mac was retired).

You can verify and install MAUI from the command line:

// Check your installed .NET version (run in a terminal)
dotnet --version

// Install the MAUI workload
dotnet workload install maui

// Create a new MAUI app
dotnet new maui -n MyFirstMauiApp

// Build and run on Windows
dotnet build -t:Run -f net10.0-windows10.0.19041.0

Important pitfall: to build and deploy to iOS you need a Mac somewhere in the pipeline (locally paired or via a CI runner) because Apple's toolchain only runs on macOS. Plan for this early if iOS is a target.

Building Your First .NET MAUI App in C#

Let's build a small counter app to understand the structure. A new MAUI project includes MauiProgram.cs (the app's entry point and DI configuration), App.xaml, and a page like MainPage.xaml.

First, the startup file where you register services and fonts:

using Microsoft.Extensions.Logging;

namespace MyFirstMauiApp;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        // Register your view models and services for dependency injection
        builder.Services.AddSingleton<MainPage>();

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}

Now define the UI in XAML. XAML is a declarative markup language for layout; if you've used HTML, the concept of nested elements will feel natural.

<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyFirstMauiApp.MainPage">
    <VerticalStackLayout Spacing="25" Padding="30" VerticalOptions="Center">
        <Label Text="Welcome to .NET MAUI"
               FontSize="28"
               HorizontalOptions="Center" />
        <Button x:Name="CounterBtn"
                Text="Click me"
                Clicked="OnCounterClicked"
                HorizontalOptions="Center" />
    </VerticalStackLayout>
</ContentPage>

And the code-behind that handles the click event:

namespace MyFirstMauiApp;

public partial class MainPage : ContentPage
{
    private int _count = 0;

    public MainPage()
    {
        InitializeComponent();
    }

    private void OnCounterClicked(object sender, EventArgs e)
    {
        _count++;
        CounterBtn.Text = $"Clicked {_count} times";

        // Announce the change for accessibility / screen readers
        SemanticScreenReader.Announce(CounterBtn.Text);
    }
}

Run it, and the same code produces a native window on Windows, a native activity on Android, and a native view controller on iOS. That's the core promise of cross-platform app development in C#.

Use the MVVM Pattern (Best Practice)

Click handlers in code-behind are fine for demos, but real apps should use the MVVM (Model-View-ViewModel) pattern. MVVM separates your UI (View) from your logic (ViewModel), making code testable and maintainable. The CommunityToolkit.Mvvm NuGet package removes nearly all the boilerplate with source generators.

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace MyFirstMauiApp.ViewModels;

public partial class CounterViewModel : ObservableObject
{
    // Generates a public 'Count' property with change notification
    [ObservableProperty]
    private int count;

    // Generates an ICommand named 'IncrementCommand'
    [RelayCommand]
    private void Increment()
    {
        Count++;
    }
}

Bind directly in XAML — no event handlers needed:

<Label Text="{Binding Count, StringFormat='Count: {0}'}" />
<Button Text="Increment"
        Command="{Binding IncrementCommand}" />

Why this matters: with MVVM, you can unit-test CounterViewModel without launching any UI. Logic stays decoupled from platform code, which is essential as your app grows.

Accessing Native Device Features

One common question is "how do I use the camera, GPS, or sensors?" MAUI ships Essentials APIs that expose native capabilities through a single C# call. For example, getting the device's location:

private async Task<string> GetCurrentLocationAsync()
{
    try
    {
        var request = new GeolocationRequest(
            GeolocationAccuracy.Medium,
            TimeSpan.FromSeconds(10));

        var location = await Geolocation.Default.GetLocationAsync(request);

        return location is null
            ? "Location unavailable"
            : $"Lat: {location.Latitude}, Long: {location.Longitude}";
    }
    catch (FeatureNotSupportedException)
    {
        return "Geolocation not supported on this device";
    }
}

The same pattern works for connectivity, secure storage, file pickers, and more — write once, run everywhere.

Common .NET MAUI Pitfalls to Avoid

Even experienced developers hit these snags. Avoid them from the start:

  • Blocking the UI thread. Always use async/await for I/O and network calls. Marshal UI updates back with MainThread.BeginInvokeOnMainThread(...).
  • Ignoring platform permissions. Camera, location, and storage require entries in each platform's manifest (AndroidManifest.xml, iOS Info.plist). Forgetting these causes silent runtime failures.
  • Overusing code-behind. Adopt MVVM early; retrofitting it later is painful.
  • Memory leaks from event handlers. Unsubscribe from events and avoid strong references that keep pages alive. Use weak event patterns or the toolkit's messaging.
  • Testing only on Windows. Layout and behavior differ across platforms. Test on real iOS and Android devices before shipping.
  • Heavy nested layouts. Deeply nested StackLayouts hurt performance. Prefer Grid and use CollectionView (never the obsolete ListView) for lists.

Advanced .NET MAUI Tips for Senior Developers

For teams scaling MAUI in production, go further than the basics:

  • Custom Handlers — extend or override platform controls cleanly without the legacy renderer overhead.
  • Native AOT & trimming — reduce app size and improve startup, especially on iOS.
  • Shell navigation — use AppShell with URI-based routing for clean, deep-linkable navigation.
  • CI/CD pipelines — automate builds with GitHub Actions or Azure DevOps, and distribute via App Center alternatives or TestFlight/Play Console.
  • Blazor Hybrid — share web and native UI when you already have a Razor component library.

Here's a quick Shell navigation example registering a route and navigating with parameters:

// Register the route once (e.g. in AppShell constructor)
Routing.RegisterRoute("details", typeof(DetailsPage));

// Navigate and pass data anywhere in your app
await Shell.Current.GoToAsync($"details?productId={id}");

Conclusion: Start Building with .NET MAUI Today

.NET MAUI in 2026 is the most productive way to build native iOS, Android, Windows, and macOS apps from one C# codebase. You've learned what MAUI is, how it improves on Xamarin, how to set up your environment, and how to build a real app using XAML, MVVM, and native device APIs — plus the pitfalls and advanced techniques that matter in production.

Key takeaways:

  • One codebase, four platforms — .NET MAUI delivers native performance, not a web wrapper.
  • Use MVVM with the CommunityToolkit.Mvvm for testable, maintainable code.
  • Lean on MAUI Essentials for camera, GPS, storage, and other native features.
  • Avoid common pitfalls — async UI, platform permissions, and lightweight layouts.
  • Test on real devices across every platform you ship.

The fastest way to learn .NET MAUI is to build something. Run dotnet new maui, open the project, and start experimenting today. Your single C# codebase is ready to reach users on every major platform.

About csharp-coder.com
Your go-to resource for C#, .NET, and modern software development. Follow along for daily tutorials, tips, and real-world examples.

Comments

Popular posts from this blog

Angular 14 CRUD Operation with Web API .Net 6.0

How to Perform CRUD Operation Using Angular 14 In this article, we will learn the angular crud (create, read, update, delete) tutorial with ASP.NET Core 6 web API. We will use the SQL Server database and responsive user interface for our Web app, we will use the Bootstrap 5. Let's start step by step. Step 1 - Create Database and Web API First we need to create Employee database in SQL Server and web API to communicate with database. so you can use my previous article CRUD operations in web API using net 6.0 to create web API step by step. As you can see, after creating all the required API and database, our API creation part is completed. Now we have to do the angular part like installing angular CLI, creating angular 14 project, command for building and running angular application...etc. Step 2 - Install Angular CLI Now we have to install angular CLI into our system. If you have already installed angular CLI into your system then skip this step.  To install angular CLI ope...

Angular 14 : 404 error during refresh page after deployment

In this article, We will learn how to solve 404 file or directory not found angular error in production.  Refresh browser angular 404 file or directory not found error You have built an Angular app and created a production build with ng build --prod You deploy it to a production server. Everything works fine until you refresh the page. The app throws The requested URL was not found on this server message (Status code 404 not found). It appears that angular routing not working on the production server when you refresh the page. The error appears on the following scenarios When you type the URL directly in the address bar. When you refresh the page The error appears on all the pages except the root page.   Reason for the requested URL was not found on this server error In a Multi-page web application, every time the application needs to display a page it has to send a request to the web server. You can do that by either typing the URL in the address bar, clicking on the Me...

Send an Email via SMTP with MailKit Using .NET 6

How to Send an Email in .NET Core This tutorial show you how to send an email in .NET 6.0 using the MailKit email client library. Install MailKit via NuGet Visual Studio Package Manager Console: Install-Package MailKit How to Send an HTML Email in .NET 6.0 This code sends a simple HTML email using the Gmail SMTP service. There are instructions further below on how to use a few other popular SMTP providers - Gmail, Hotmail, Office 365. // create email message var email = new MimeMessage(); email.From.Add(MailboxAddress.Parse("from_address@example.com")); email.To.Add(MailboxAddress.Parse("to_address@example.com")); email.Subject = "Email Subject"; email.Body = new TextPart(TextFormat.Html) { Text = "<h1>Test HTML Message Body</h1>" }; // send email using var smtp = new SmtpClient(); smtp.Connect("smtp.gmail.com", 587, SecureSocketOptions.StartTls); smtp.Authenticate("[Username]", "[Password]"); smtp.Se...