Skip to content

Runtime theme desyncs from config when the color scheme changes between startup reads #760

Closed

Description

@LouisHaftmann

What happened?

Uniwind.setTheme('light') is called and Uniwind.currentTheme reports light, but every className resolves against the dark theme for the whole session. In production this happens on Android devices in system dark mode, on some cold starts.

Actual With the fix below

Root cause: the theme is held in two places, and each reads Appearance.getColorScheme() on its own:

  • UniwindRuntime.currentThemeName (core/native/runtime.ts), read when the runtime module is evaluated. Expo's startup modules import react-native, which Uniwind redirects to its components, so this happens before the app entry runs.
  • #currentTheme in UniwindConfigBuilder (core/config/config.common.ts), read when Uniwind is first accessed, e.g. in the root layout.

The runtime is only updated in onThemeChange(), which runs when the config's theme changes. If the scheme changes between the two reads, they disagree and nothing reconciles them:

runtime.ts reads Appearance          -> "dark"
scheme becomes "light"
config constructed, reads Appearance -> "light"
setTheme('light'): prevTheme === "light", onThemeChange() never runs
=> runtime.currentThemeName stays "dark"

On Android the scheme changes between the reads without any app code. The bundle starts evaluating before the Activity exists, so AppearanceModule.getColorScheme() falls back to the application context (system: dark). Then the Activity resumes with MODE_NIGHT_NO (userInterfaceStyle: "light") and reports light. The repro triggers the same ordering deterministically with Appearance.setColorScheme('light') in the entry file.

Suggested fix (verified in the repro), in src/core/config/config.native.ts:

 constructor() {
     super()
+    UniwindStore.runtime.currentThemeName = this.currentTheme
 }

An alternative is for runtime.ts to take its initial theme from the config instead of reading Appearance itself, so there is a single source of truth.

Steps to Reproduce

  1. Clone the repro and run pnpm install.
  2. Put the Android emulator in system dark mode.
  3. Run pnpm android (Expo Go).
  4. The screen shows Uniwind.currentTheme: light, but with a black background and white text. Expected: white background, black text.

Snack or Repository Link

https://github.com/LouisHaftmann/uniwind-theme-race-repro

Uniwind version

1.13.1

React Native Version

0.88.1

Platforms

Android

Expo

Yes

Additional information 〰

Activity

  1. self-assigned this
    on Nov 3, 2026
  2. added
    bugSomething isn't working
    pending-releaseFixed, waiting for new version to be released
    and removed on Nov 3, 2026
  3. github-actions commented on Nov 5, 2026

    @github-actions
    Contributor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions