Skip to content

Scoped Theme

Scoped Theme

There are cases where you may want to render specific components or screens with a fixed theme. For instance, a Camera view might require a dark background for better contrast, even if the user has selected light mode for the app. Other examples include modals, dialogs, or enabling users to preview the app in different themes to choose their preferred one.

To address this, Unistyles 3.0 introduces the concept of a Scoped Theme, which allows you to assign a fixed theme to a specific component or screen.

Usage with named theme

To use scoped theme, you need to import ScopedTheme component from react-native-unistyles:

import { ScopedTheme } from 'react-native-unistyles'

Scoped theme accepts one of your registered theme names as a prop:

<ScopedTheme name="dark">
// components here will be fixed to dark theme
<View style={styles.container}>
<Text style={styles.text}>
Hello world
</Text>
</View>
</ScopedTheme>

You can also nest ScopedTheme components:

<ScopedTheme name="dark">
// I will be dark!
<View style={styles.container}>
<Text style={styles.text}>
Dark
</Text>
</View>
<ScopedTheme name="light">
// I will be light!
<View style={styles.container}>
<Text style={styles.text}>
Light
</Text>
</View>
</ScopedTheme>
// I will be dark again!
<View style={styles.container}>
<Text style={styles.text}>
Dark
</Text>
</View>
</ScopedTheme>

Usage with inverted adaptive theme

You can also use ScopedTheme with the invertedAdaptive prop. This prop cannot be used together with a named ScopedTheme, as these options are mutually exclusive. The purpose of invertedAdaptive is to apply the opposite adaptive theme to the one that is currently active.

In other words, if your app supports adaptive themes and you use ScopedTheme with the invertedAdaptive prop, it will apply:

the dark theme when the color scheme is light
the light theme when the color scheme is dark

Use Cases:

The invertedAdaptive prop is useful in scenarios where you want to highlight a specific section of your app by contrasting it with the current theme. For example:

  • Modal dialogs or popups: Make a modal stand out by using the opposite theme, drawing the user’s attention
  • Preview components: Show users how your app looks in both light and dark modes by inverting the theme for a preview section
  • Special content areas: Emphasize warnings, tips, or promotional banners by displaying them with a contrasting theme

By using invertedAdaptive, you can create visually distinct areas in your app that improve user experience and accessibility.

<ScopedTheme invertedAdaptive>
<View style={styles.container}>
<Text style={styles.text}>
Text is light when color scheme is dark and dark when color scheme is light
</Text>
</View>
</ScopedTheme>

You can also nest other ScopedThemes inside ScopedTheme with invertedAdaptive prop.

Reset

If you wrap multiple children in ScopedTheme you can disable scoped theme for some of them by using reset prop:

<ScopedTheme name="dark">
<View style={styles.container}>
<Text style={styles.text}>
I will be dark!
</Text>
</View>
<ScopedTheme reset>
<View style={styles.container}>
<Text style={styles.text}>
I will be light again!
</Text>
</View>
</ScopedTheme>
<View style={styles.container}>
<Text style={styles.text}>
I'm dark again
</Text>
</View>
</ScopedTheme>

Reading current scoped theme

Information about the current ScopedTheme is temporary and only available during the component render phase.

For the following example, themeName will be different based on the place where we access it:

import { UnistylesRuntime, ScopedTheme } from 'react-native-unistyles'
const MyComponent = () => {
// themeName will be 'light' here πŸ’₯
const themeName = UnistylesRuntime.themeName
return (
<ScopedTheme name="dark">
<ScopedText>
I'm scoped
</ScopedText>
</ScopedTheme>
)
}
const ScopedText = ({ children }) => {
// themeName will be 'dark' here βœ…
// because we're "inside" of the ScopedTheme
const themeName = UnistylesRuntime.themeName
return (
<Text style={styles.text}>
{children}
</Text>
)
}

If you want to react to changes in the scoped theme, you can use the useUnistyles hook or the withUnistyles helper:

import { useUnistyles, ScopedTheme } from 'react-native-unistyles'
// My parent is wrapped with ScopedTheme invertedAdaptive
const ScopedComponent = () => {
// reading themeName from `useUnistyles` will always log
// correctly parent scoped theme name 🀯
const { rt } = useUnistyles()
return (
<Text>
{rt.themeName} // light for dark mode, dark for light mode
</Text>
)
}
// JSX
<ScopedTheme invertedAdaptive>
<ScopedComponent />
</ScopedTheme>

Same goes for the withUnistyles helper:

import { withUnistyles, ScopedTheme } from 'react-native-unistyles'
const ScopedTextInput = withUnistyles(TextInput, (theme, rt) => ({
// I will always take in count parent scoped theme
color: rt.themeName === 'light'
? theme.colors.text
: theme.colors.background
}))
// My parent is wrapped with ScopedTheme invertedAdaptive
const ScopedComponent = () => {
return (
<ScopedTextInput />
)
}
// JSX
<ScopedTheme invertedAdaptive>
<ScopedComponent />
</ScopedTheme>