Articles · How-To GuidesUpdated October 2026

Add dark mode to an app by following the system setting, and keep the in-app toggle as the extra.

The job is to follow the device's appearance setting. Both platforms assume an app reads whether the system is light or dark and repaints itself, and both treat a Light, Dark and System choice inside your app as an extra you may add later. No permission is involved. There is nothing to request and nothing for the user to refuse, because the choice is already made in the system settings and your app only has to read it and react when it changes. On iOS most of that happens for you. On Android almost none of it does, and that gap is the only real difficulty. The rest is colour and contrast, which is why this sits on top of app design basics.

This page covers the iOS key that silently switches dark mode off, the Android theme parent without which nothing happens, the two in-app override calls and the version each one needs, and the five places a dark build breaks. There is also one API on the iOS side that most pages still get wrong. Tick the boxes further down and the checklist says which steps apply to your app.

See which steps apply to your app

The short version

Dark mode is a theme and a declaration not a permission and not a prompt.

Nothing about dark mode asks the user for anything. There is no permission dialog, no entry in a privacy screen, no denial path to handle. What you declare instead is a theme parent on Android and, on iOS, an optional key in Info.plist. The nearest thing to a refusal is somebody leaving their device in light mode, and the correct response to that is to render light.

That makes it unlike most feature work, because there is no fallback to write for a user who said no. There is one wrong outcome: a screen that keeps a hardcoded colour and ends up white on white, or black on black. Everything below is aimed at finding those screens before a user does.

What both platforms expect before you write anything

Follow the system setting first. Apple's Human Interface Guidelines, read on 3 October 2026, are blunt about it: they advise against offering an app-specific appearance setting at all, because it makes people adjust more than one setting to get the appearance they want, and because an app that does not respond to the systemwide choice looks broken. Android does offer an in-app choice, and names System default as the recommended default. Neither platform treats your own toggle as the main job.

The setting also moves while your app is running. Apple notes that people can pick an Auto appearance that switches as conditions change through the day, potentially while your app is open, so a correct app reacts to the change rather than reading the value once at launch. Android has automatic modes too, and one of them, MODE_NIGHT_AUTO, switches based on the device's current location and certain other sensors. That one you cannot judge on an emulator.

Apple publishes contrast numbers for this, which is rare. Keep the contrast ratio between colours no lower than 4.5 to 1, and for custom foreground and background colours strive for 7 to 1, especially in small text. Those are the figures in the guidance as read on 3 October 2026. Apple also warns that dark mode with Increase Contrast turned on can reduce the contrast between dark text and a dark background, so test that combination deliberately.

Two scope limits before you promise this to anyone. Apple's guidance says dark mode is not supported in visionOS or watchOS, so a watch companion is not part of the work. And on Android the feature arrived in Android 10, API level 29, so anything older needs the AndroidX path rather than the platform one.

Apple Human Interface Guidelines, Dark Mode, read 3 October 2026

On iOS it is colours, one key, and one API that has moved

The system assumes any app linked against the iOS 13 SDK or later supports both appearances, and standard views and controls update themselves. Apple's first instruction is to turn dark mode on and see how the app responds before changing any code, because an app built from standard components may need very little. The colour rules and the key below come from Apple's Supporting Dark Mode in your interface article and the UIUserInterfaceStyle reference, both read on 3 October 2026.

The code work is making colours adapt, and there are two documented ways to do it. Use semantic colours, named for their intended purpose rather than a specific value, and they render with values appropriate to the current setting. Or add a Color Set asset to the asset catalog with light and dark variants, plus the Any Appearance variant for older systems. A colour built from fixed component values does not adapt, so a CGColor assigned once at creation time stays whatever it was, and that assignment has to move into code that runs when the appearance changes.

Here is where a current page differs from an old one. Apple's Supporting Dark Mode article still names traitCollectionDidChange as the method to put that code in, and most tutorials copy it. The reference page for that method, read on 3 October 2026, marks it deprecated as of iOS 17 and says to use the trait change registration APIs declared in the UITraitChangeObservable protocol instead, meaning registerForTraitChanges with a handler or with a target and an action. If you are reading an answer that only mentions traitCollectionDidChange, it predates iOS 17. The other hooks in that article, among them layoutSubviews, draw and tintColorDidChange, are unchanged.

The key that catches people is UIUserInterfaceStyle in Info.plist, which Xcode shows as Appearance. It is a string with three documented values. Automatic is the default and adopts the systemwide style while observing changes to it. Light forces the light style and Dark forces the dark one, and either value makes your app ignore every systemwide change from then on. Apple describes setting it to Light as a temporary opt out while you work on your dark mode support, not a setting to ship. If dark mode appears to do nothing in your build, look here first. The one remaining iOS-specific piece is depth: in dark mode the system uses two sets of background colours, base and elevated, so a sheet appears to advance while the screen behind it recedes, which is why the guidance is to prefer the system background colours and the materials and vibrancy behind apple liquid glass design.

On Android nothing happens until the theme inherits DayNight

This is the harder platform and it is not close. On Android dark theme is opt in at the theme level. Set your app's theme, usually in res/values/styles.xml, to inherit from a DayNight parent: either Theme.AppCompat.DayNight or Theme.MaterialComponents.DayNight. That ties the app to the system-controlled night mode flags and gives it a default dark theme when night mode is enabled. Without it, the device goes dark and your app stays light.

Then the colours. The guidance is to avoid hardcoded colours and icons intended for a light theme, and to use theme attributes or night-qualified resources instead. Two attributes do most of the work: textColorPrimary, a general-purpose text colour that is near black in light and near white in dark, and colorControlNormal for icons. Both carry a disabled state. Material Design Components adds colorSurface and colorOnSurface.

There is a shortcut and it has conditions. Force Dark, added in Android 10, analyses each view of a light-themed app and applies a dark theme automatically before the view is drawn. You opt in with android:forceDarkAllowed set to true in the activity's theme, and you can switch it off for a single view with the same attribute in a layout or with setForceDarkAllowed. Two conditions catch people out: it is not applied if your app's theme is already dark, and not applied if your theme inherits from DayNight, because that switches by itself. Google says to test thoroughly and exclude views as needed.

If you do offer the in-app choice, Android documents three options, Light, Dark and System default, mapping to MODE_NIGHT_NO, MODE_NIGHT_YES and MODE_NIGHT_FOLLOW_SYSTEM. On API level 31 and above use UiModeManager.setApplicationNightMode, which lets the system match your theme during the splash screen and persists the choice until your app changes it, the user clears the app's data, or the app is uninstalled. On API level 30 and below use AppCompatDelegate.setDefaultNightMode. Three radio rows is all the interface it needs, so it belongs with the other switches you build when you add settings screen.

Android Developers, Dark theme, read 3 October 2026

The five places a dark build actually breaks

Flipping the theme is not the end of it, and the first surprise is a lifecycle one. On Android a theme change triggers a uiMode configuration change, so activities are automatically recreated and anything held in one without being saved is gone. Since AppCompat 1.1.0 the setDefaultNightMode call recreates started activities itself. If that is wrong for you, for instance while a video is playing, declare android:configChanges="uiMode" on the activity and handle onConfigurationChanged yourself. On iOS there is no recreation: the system updates the trait environment and asks every window and view to redraw, and Apple's instruction is to keep that code quick.

After that it is the surfaces you do not fully control. Launch screens are drawn before your code runs, and on Android the guidance is to replace a programmatically set white background with the colorBackground theme attribute, with the warning that dark-themed windowBackground drawables only work on Android 10. Notifications and launcher widgets render inside somebody else's theme, so use the system notification templates and test any custom view in both. Web content in a web view is its own task, documented separately by Android. Images are the fifth: a picture with a white background glows in a dark context, and Apple suggests darkening it slightly rather than shipping it as it is.

Almost all of this is testable without a device. The appearance is a system setting, so a simulator or an emulator will flip it, and that exercises the whole code path including the configuration change. Two things do not survive that. How a near black surface looks on the panel in somebody's hand is a judgement you can only make on hardware. And Android's MODE_NIGHT_AUTO switches on the device's current location and certain other sensors, so an emulator will not show it behaving.

The check that fails quietly is contrast. Dark mode moves every foreground and background pair in the app at once, so a grey that passed in light may fail in dark, and the pairs that break are rarely the ones you looked at. Run the numbers in both appearances, with the accessibility settings on as well as off, which is the point where this turns into making it readable for everyone.

Check the ones that apply

Which dark mode steps your app needs

Two steps apply to everyone: inherit a DayNight theme on Android, and leave UIUserInterfaceStyle out of Info.plist or set to Automatic on iOS. The rest depends on what is already in the app.

2 steps in total

Nothing checked, so the theme and the declaration are the whole job. Turn the device setting on and walk every screen before you write any code.

What each platform asks of you, and what it asks of the user

The jobiOSAndroidAsks the user for anything
Following the system appearanceAutomatic for any app linked against the iOS 13 SDK or laterOnly once the app theme inherits from a DayNight parentNo
Where an app-wide override is declaredUIUserInterfaceStyle in Info.plist: Automatic, Light or DarkThe theme parent in res/values/styles.xml, plus night-qualified resourcesNo
The in-app Light, Dark and System switchoverrideUserInterfaceStyle on a window, view or view controllersetApplicationNightMode on API 31 and up, setDefaultNightMode below itNo
Darkening a light-only interface automaticallyNo equivalent in the Apple documentation read for this pageForce Dark, from Android 10, opt in with android:forceDarkAllowedNo
What a change costs at runtimeWindows and views are asked to redraw, with no restartA uiMode configuration change, so activities are recreatedNo

Getting it right in an app you own

Dark mode is cheap to start and annoying to finish. The pattern that works is to define every colour once, in one place, with a light value and a dark value, and have every screen read from there. The finish is a walk through the whole app in both appearances with the sheets open and the keyboard up. That part is slow, and it is the only thing that finds the screen somebody hardcoded in a hurry.

In a React Native and TypeScript project the read is one hook. The useColorScheme hook returns light, dark or null, and updates when the setting changes, including on a schedule. The Appearance module exposes getColorScheme alongside setColorScheme, which overrides the appearance for your application only, does not touch the system setting or other apps, and takes auto to hand control back. Those are the current names in the React Native documentation as read on 3 October 2026. The native declarations above still win: an Info.plist forced to Light will beat your hook every time.

Newly is an AI app builder. You describe the app in plain English and it writes a real React Native and TypeScript project you own, runs it on cloud iOS and Android simulators while it builds, and ships it to TestFlight and to Google Play internal testing. It is $25 a month and there is no free plan. Dark mode is worth asking for in the first prompt rather than the twentieth, because asking once across five screens is a different job from retrofitting it across fifty.

Questions people ask about dark mode

No. There is no permission on either platform and no prompt for the user to accept or refuse. Dark mode is a theme on Android and, on iOS, an optional Info.plist declaration. The choice is already made in the system settings, and your app's job is to read it and repaint. Nothing in Apple's Dark Mode guidance or Android's Dark theme guide, both read on 3 October 2026, describes a request made to the user.

Describe the app in both appearances

Write down which colours pair with which, which surfaces are dark, and whether you want a Light, Dark and System row in settings at all, then build it and look at every screen in both appearances before anything else gets added.

Start building