.NET MAUI Theming
Two main approaches exist for theming MAUI apps: AppThemeBinding (automatic OS theme response) and ResourceDictionary theming (custom themes with runtime switching). They can be combined.
- AppThemeBinding (OS Theme Response)
AppThemeBinding is a markup extension that selects a value based on the current system theme.
Properties
Property Description
Light
Value used when the device is in light mode
Dark
Value used when the device is in dark mode
Default
Fallback value when the device theme is unknown
XAML Usage
<Label Text="Themed text" TextColor="{AppThemeBinding Light=Green, Dark=Red}" BackgroundColor="{AppThemeBinding Light=White, Dark=Black}" />
<!-- With StaticResource or DynamicResource values --> <Label TextColor="{AppThemeBinding Light={StaticResource LightPrimary}, Dark={StaticResource DarkPrimary}}" />
C# Extension Methods
var label = new Label(); // Color-specific helper label.SetAppThemeColor(Label.TextColorProperty, Colors.Green, Colors.Red);
// Generic helper for any type label.SetAppTheme<Color>(Label.TextColorProperty, Colors.Green, Colors.Red);
- ResourceDictionary Theming (Custom Themes)
Use separate ResourceDictionary files with matching keys to define themes, then swap them at runtime.
Define Theme Dictionaries
Each ResourceDictionary must have a code-behind file that calls InitializeComponent() .
LightTheme.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyApp.Themes.LightTheme"> <Color x:Key="PageBackgroundColor">White</Color> <Color x:Key="PrimaryTextColor">#333333</Color> <Color x:Key="AccentColor">#2196F3</Color> </ResourceDictionary>
LightTheme.xaml.cs
namespace MyApp.Themes;
public partial class LightTheme : ResourceDictionary { public LightTheme() => InitializeComponent(); }
DarkTheme.xaml.cs — same pattern, different values, same keys.
Consume with DynamicResource
Use DynamicResource (not StaticResource ) so values update at runtime when the dictionary changes:
<ContentPage BackgroundColor="{DynamicResource PageBackgroundColor}"> <Label Text="Hello" TextColor="{DynamicResource PrimaryTextColor}" /> <Button Text="Action" BackgroundColor="{DynamicResource AccentColor}" /> </ContentPage>
Switch Themes at Runtime
void ApplyTheme(ResourceDictionary theme) { var mergedDictionaries = Application.Current!.Resources.MergedDictionaries; mergedDictionaries.Clear(); mergedDictionaries.Add(theme); }
// Usage ApplyTheme(new LightTheme()); ApplyTheme(new DarkTheme());
System Theme Detection
Current Theme
AppTheme currentTheme = Application.Current!.RequestedTheme; // Returns AppTheme.Light, AppTheme.Dark, or AppTheme.Unspecified
Override the System Theme
// Force a specific theme regardless of OS setting Application.Current!.UserAppTheme = AppTheme.Dark;
// Reset to follow the system theme Application.Current!.UserAppTheme = AppTheme.Unspecified;
React to Theme Changes
Application.Current!.RequestedThemeChanged += (s, e) => { AppTheme newTheme = e.RequestedTheme; // Update UI or switch ResourceDictionaries };
Combining Both Approaches
Use AppThemeBinding with DynamicResource values for maximum flexibility:
<Label TextColor="{AppThemeBinding Light={DynamicResource LightPrimary}, Dark={DynamicResource DarkPrimary}}" />
Or react to system changes and swap full ResourceDictionaries:
Application.Current!.RequestedThemeChanged += (s, e) => { ApplyTheme(e.RequestedTheme == AppTheme.Dark ? new DarkTheme() : new LightTheme()); };
Platform Support & Gotchas
Platform Minimum Version
iOS 13+
Android 10+ (API 29)
macOS Catalyst 10.14+
Windows 10+
Android: ConfigChanges.UiMode
MainActivity must include ConfigChanges.UiMode or theme change events will not fire and the activity may restart instead:
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode // Required for theme detection | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] public class MainActivity : MauiAppCompatActivity { }
CSS Theming Limitation
MAUI supports CSS styling, but CSS-based themes cannot be swapped dynamically at runtime. Use ResourceDictionary theming instead for dynamic theme switching.
Quick Reference
-
OS light/dark → AppThemeBinding markup extension
-
Theme colors in C# → SetAppThemeColor() , SetAppTheme<T>()
-
Read OS theme → Application.Current.RequestedTheme
-
Force theme → Application.Current.UserAppTheme = AppTheme.Dark
-
Theme changes → RequestedThemeChanged event
-
Custom switching → Swap ResourceDictionary in MergedDictionaries
-
Runtime bindings → DynamicResource (not StaticResource )