React Native has two ways for you to determine a user's appearance preferences:
- The Appearance API which lets you get their current color scheme
- The useColorScheme hook which provides an up-to-date color scheme (it will automatically update as the user's preferences change)
This post was originally posted on React Native School. Visit React Native School to access 170+ tutorials and 18 courses!
Today we'll be leveraging the useColorScheme hook.
We'll take a look at the following screen which shows black text and a white background - a light color scheme.
import React from "react"
import { View, Text, ViewStyle, TextStyle, StyleSheet } from "react-native"
const App = () => {
const viewStyles: ViewStyle[] = [
styles.container,
{ backgroundColor: "white" },
]
const textStyles: TextStyle[] = [styles.text, { color: "black" }]
return (
<View style={viewStyles}>
<Text style={textStyles}>Hello, world!</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
text: {
fontWeight: "bold",
fontSize: 20,
},
})
export default App
We can use the useColorScheme hook and determine the user's preferred color scheme, changing our colors based on it.
import React from "react"
import {
View,
Text,
ViewStyle,
TextStyle,
StyleSheet,
useColorScheme,
} from "react-native"
const App = () => {
const colorScheme = useColorScheme()
const isLightTheme = colorScheme === "light"
const viewStyles: ViewStyle[] = [
styles.container,
{ backgroundColor: isLightTheme ? "white" : "black" },
]
const textStyles: TextStyle[] = [
styles.text,
{ color: isLightTheme ? "black" : "white" },
]
return (
<View style={viewStyles}>
<Text style={textStyles}>Hello, world!</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
text: {
fontWeight: "bold",
fontSize: 20,
},
})
export default App
NOTE: If you have the chrome debugger enabled
useColorSchemewill always return 'light'.
Improving Reusability with useColorScheme
Currently our component needs to know the color theme to determine what color to use. We can put our app colors into a Colors object where we define colors based on the theme.
Then, in the component, we can just say we want to use colors.background or colors.text. Something descriptive.
import React from "react"
import {
View,
Text,
ViewStyle,
TextStyle,
StyleSheet,
useColorScheme,
} from "react-native"
const Colors = {
light: {
background: "white",
text: "black",
},
dark: {
background: "black",
text: "white",
},
}
const App = () => {
const colorScheme = useColorScheme()
const colors = Colors[colorScheme]
const viewStyles: ViewStyle[] = [
styles.container,
{ backgroundColor: colors.background },
]
const textStyles: TextStyle[] = [styles.text, { color: colors.text }]
return (
<View style={viewStyles}>
<Text style={textStyles}>Hello, world!</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
text: {
fontWeight: "bold",
fontSize: 20,
},
})
export default App
TypeScript Tip: Dealing with useColorScheme null Type
You may be seeing a warning when trying to access Colors[colorScheme] because colorScheme may be null...
This is a nice tip I saw in an Expo Template on how to fix this.
We can create a custom useColorScheme hook that changes the return type to not be null via NonNullable.
// hooks/ColorSchemeName.ts
import {
ColorSchemeName,
useColorScheme as _useColorScheme,
} from "react-native"
// The useColorScheme value is always either light or dark, but the built-in
// type suggests that it can be null. This will not happen in practice, so this
// makes it a bit easier to work with.
export default function useColorScheme(): NonNullable<ColorSchemeName> {
return _useColorScheme() as NonNullable<ColorSchemeName>
}
Creating your own useColorScheme hook is also nice if you want to "fake" a different color scheme/quickly change through them. Rather than returning the result of _useColorScheme you can just return dark or light.
Custom useThemeColors Hook
Finally, in an effort to write even more reusable code, we can simplify our logic further and create a useThemeColors hook to return just the colors of the current theme without the component having to be aware of what the current theme is.
// hooks/useThemeColors.ts
import useColorScheme from "hooks/useColorScheme"
const Colors = {
light: {
background: "white",
text: "black",
},
dark: {
background: "black",
text: "white",
},
}
const useThemeColors = () => {
const colorScheme = useColorScheme()
const colors = Colors[colorScheme]
return colors
}
export default useThemeColors
Further reading: how to set up path alias', like shown above, in a React Native + TypeScript project.
Now your App.tsx can be as simple as
import React from "react"
import { View, Text, ViewStyle, TextStyle, StyleSheet } from "react-native"
import useThemeColors from "hooks/useThemeColors"
const App = () => {
const colors = useThemeColors()
const viewStyles: ViewStyle[] = [
styles.container,
{ backgroundColor: colors.background },
]
const textStyles: TextStyle[] = [styles.text, { color: colors.text }]
return (
<View style={viewStyles}>
<Text style={textStyles}>Hello, world!</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
text: {
fontWeight: "bold",
fontSize: 20,
},
})
export default App
Now within our component we don't have to do anything to figure out the current theme or which colors to use for that them. The useThemeColors hook encapsulates all of that and returns a color object from which we can choose the right color for our UI elements.
Expanded Example
You can find an expanded version of this code in a more real-world example in React Native School's Clock example app.

Top comments (0)