react-native-web
react-native-web copied to clipboard
Appearance.setColorScheme not workin in RN Web
Is there an existing issue for this?
- [X] I have searched the existing issues
Describe the issue
Appearance.setColorScheme("dark") works perfectly fine for iOS and Android but raises the following Error when used for RN web even thought its written in the documentation that it works for web.
_Appearance.default.setColorScheme is not a function. (In '_Appearance.default.setColorScheme("dark")', '_Appearance.default.setColorScheme' is undefined)
Expected behavior
It should change the colorScheme.
Steps to reproduce
1.Create a Pressable 2. onPress={Appearance.setColorScheme("dark")} 3. Observe error in RN web
Test case
https://snack.expo.dev/@hadi_ea/setcolorscheme-error
Additional comments
Error:
Uncaught Error _Appearance.default.setColorScheme is not a function. (In '_Appearance.default.setColorScheme("dark")', '_Appearance.default.setColorScheme' is undefined) Call Stack
Source Map This call stack is not symbolicated. Some features are unavailable such as viewing the function name or tapping to open files. index http://localhost:8081/node_modules/expo-router/entry.bundle:153573:52 renderWithHooks http://localhost:8081/node_modules/expo-router/entry.bundle:25695:33 mountIndeterminateComponent http://localhost:8081/node_modules/expo-router/entry.bundle:28984:34 beginWork$1 http://localhost:8081/node_modules/expo-router/entry.bundle:35239:29 performUnitOfWork http://localhost:8081/node_modules/expo-router/entry.bundle:34509:29 workLoopSync http://localhost:8081/node_modules/expo-router/entry.bundle:34432:28 renderRootSync http://localhost:8081/node_modules/expo-router/entry.bundle:34405:25 recoverFromConcurrentError http://localhost:8081/node_modules/expo-router/entry.bundle:33897:40 performConcurrentWorkOnRoot http://localhost:8081/node_modules/expo-router/entry.bundle:33810:54 workLoop http://localhost:8081/node_modules/expo-router/entry.bundle:37546:48 flushWork http://localhost:8081/node_modules/expo-router/entry.bundle:37524:28 performWorkUntilDeadline http://localhost:8081/node_modules/expo-router/entry.bundle:37761:48 Showing all frames Component Stack
Source Map
This call stack is not symbolicated. Some features are unavailable such as viewing the function name or tapping to open files.
index
http://localhost:8081/node_modules/expo-router/entry.bundle:153566:43
Route
http://localhost:8081/node_modules/expo-router/entry.bundle:65953:44
Route
http://localhost:8081/node_modules/expo-router/entry.bundle:66319:41
StaticContainer
http://localhost:8081/node_modules/expo-router/entry.bundle:165743:17
EnsureSingleNavigator
http://localhost:8081/node_modules/expo-router/entry.bundle:163244:34
SceneView
http://localhost:8081/node_modules/expo-router/entry.bundle:165668:13
View
http://localhost:8081/node_modules/expo-router/entry.bundle:39052:26
View
http://localhost:8081/node_modules/expo-router/entry.bundle:39052:26
Background
http://localhost:8081/node_modules/expo-router/entry.bundle:167963:43
Screen
http://localhost:8081/node_modules/expo-router/entry.bundle:169069:73
View
http://localhost:8081/node_modules/expo-router/entry.bundle:39052:26
Same here, any workaround ?
I managed it by using a global state management (Zustand) and listen to changes and change color accordingly but it’s not that satisfying.
Not working too with storybook web
if (Platform.OS === 'web') {
Appearance.setColorScheme = (scheme) => {
document.documentElement.setAttribute('data-theme', scheme);
};
Appearance.getColorScheme = () => {
const systemValue = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
const userValue = document.documentElement.getAttribute('data-theme');
return userValue && userValue !== 'null' ? userValue : systemValue;
};
Appearance.addChangeListener = (listener) => {
// Listen for changes of system value
const systemValueListener = (e) => {
const newSystemValue = e.matches ? 'dark' : 'light';
const userValue = document.documentElement.getAttribute('data-theme');
listener({
colorScheme: userValue && userValue !== 'null' ? userValue : newSystemValue,
});
};
const systemValue = window.matchMedia('(prefers-color-scheme: dark)');
systemValue.addEventListener('change', systemValueListener);
// Listen for changes of user set value
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.attributeName === 'data-theme') {
listener({ colorScheme: Appearance.getColorScheme() });
}
}
});
observer.observe(document.documentElement, { attributes: true });
function remove() {
systemValue.removeEventListener('change', systemValueListener);
observer.disconnect();
}
return { remove };
};
}
Was able to use this as a workaround, behavior is equivalent to iOS and android. Uses the system dark/light settings until setColorScheme is called, in which it will always use the user-defined value.
I'm using this in the meantime. Seems to work fine.
import { useColorScheme } from 'react-native'
import { create } from 'zustand'
const useStore = create<{
isDarkMode: boolean | undefined
setIsDarkMode: (isDarkMode: boolean | undefined) => void
}>(set => ({
isDarkMode: undefined,
setIsDarkMode: isDarkMode => set({ isDarkMode }),
}))
const useOverrideValue = () => useStore(s => s.isDarkMode)
const useSetOverrideValue = () => useStore(s => s.setIsDarkMode)
export const useIsDarkMode = () => {
const fallback = useColorScheme()
const override = useOverrideValue()
const setOverride = useSetOverrideValue()
return [override ?? fallback === 'dark', setOverride] as const
}
Any idea why this is still an open issue? The function itself is just missing in total, while the docs state differently. Seems like a missing implementation. Should the docs show that it is not supported for web in the meantime?
I'm having the same issue. Very perplexed that this issue has been open for a year. The docs indicate that Appearance.setColorScheme should work.
May I request fixing this bug or updating the docs to indicate that setColorScheme does not appear on the Appearance object in web?
https://reactnative.dev/docs/appearance?guide=web
I'm using @YoussefHenna code, with a little modification:
// On my machine `matchMedia` returns a `MediaQueryList`, so `systemValue` is always `dark`.
const systemValue = window.matchMedia('(prefers-color-scheme: dark)') ? 'dark': 'light';
// fixed by using
const systemValue = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark': 'light';