Available in Uniwind 1.4.0+
Overview
ScopedTheme lets you apply a theme to a specific subtree without changing the global app theme. This is useful for previews, isolated surfaces (cards, modals, panels), and mixed-theme layouts where multiple themes need to exist on screen at the same time.
The nearest ScopedTheme wins, so nested scopes work naturally.
ScopedTheme is a runtime theme boundary. Components, hooks, and withUniwind wrapped components inside the boundary resolve styles against the scoped theme.
Usage
Basic Example
import { ScopedTheme } from 'uniwind'
import { Text , View } from 'react-native'
export function Example () {
return (
< View className = "gap-4" >
< View className = "p-4 rounded-xl bg-base" >
< Text className = "text-default" > Uses the current app theme </ Text >
</ View >
< ScopedTheme theme = "dark" >
< View className = "p-4 rounded-xl bg-base" >
< Text className = "text-default" > This subtree uses the dark theme </ Text >
< View className = "mt-2 size-8 rounded bg-primary" />
</ View >
</ ScopedTheme >
</ View >
)
}
Common Patterns
Side-by-Side Theme Previews
import { ScopedTheme } from 'uniwind'
import { Text , View } from 'react-native'
export function ThemePreviewRow () {
return (
< View className = "flex-row gap-3" >
< ScopedTheme theme = "light" >
< View className = "flex-1 p-4 bg-base rounded-2xl gap-2" >
< Text className = "text-default font-bold" > Light </ Text >
< View className = "size-8 rounded-lg bg-primary" />
< Text className = "text-default/60 text-xs" > Scoped to light </ Text >
</ View >
</ ScopedTheme >
< ScopedTheme theme = "dark" >
< View className = "flex-1 p-4 bg-base rounded-2xl gap-2" >
< Text className = "text-default font-bold" > Dark </ Text >
< View className = "size-8 rounded-lg bg-primary" />
< Text className = "text-default/60 text-xs" > Scoped to dark </ Text >
</ View >
</ ScopedTheme >
< ScopedTheme theme = "coffee" >
< View className = "flex-1 p-4 bg-base rounded-2xl gap-2" >
< Text className = "text-default font-bold" > Coffee </ Text >
< View className = "size-8 rounded-lg bg-primary" />
< Text className = "text-default/60 text-xs" > Scoped to coffee (custom theme) </ Text >
</ View >
</ ScopedTheme >
</ View >
)
}
Nested Scoped Themes
import { ScopedTheme } from 'uniwind'
import { Text , View } from 'react-native'
export function NestedScopes () {
return (
< ScopedTheme theme = "coffee" >
< View className = "w-full p-4 bg-base rounded-2xl gap-3" >
< Text className = "text-default font-bold" > Coffee (outer custom theme) </ Text >
< ScopedTheme theme = "dark" >
< View className = "w-full p-4 bg-base rounded-xl gap-3" >
< Text className = "text-default font-bold" > Dark (middle) </ Text >
< ScopedTheme theme = "light" >
< View className = "w-full p-3 bg-base rounded-lg gap-2" >
< Text className = "text-default font-bold" > Light (inner) </ Text >
< View className = "size-6 rounded bg-primary" />
</ View >
</ ScopedTheme >
</ View >
</ ScopedTheme >
</ View >
</ ScopedTheme >
)
}
Nested scopes are great for documentation previews and component galleries where you want to compare variants without changing global theme state.
Hooks and withUniwind in a Scoped Theme
Hooks and HOCs resolve against the closest ScopedTheme boundary.
useUniwind() returns the scoped theme name
useResolveClassNames() resolves class names using scoped theme variables
useCSSVariable() reads CSS variables from the scoped theme
withUniwind() mappings use values resolved from the scoped theme
Example
import { Feather } from '@expo/vector-icons'
import { Pressable , Text , View } from 'react-native'
import {
ScopedTheme ,
useCSSVariable ,
useResolveClassNames ,
useUniwind ,
withUniwind ,
} from 'uniwind'
const StyledFeather = withUniwind ( Feather , {
size: {
fromClassName: 'className' ,
styleProperty: 'width' ,
},
color: {
fromClassName: 'className' ,
styleProperty: 'color' ,
},
})
function UseUniwindExample () {
const { theme } = useUniwind ()
return (
< View className = "w-full p-4 bg-base rounded-2xl gap-1" >
< Text className = "text-default font-bold" > useUniwind </ Text >
< Text className = "text-primary" > Current theme: { theme } </ Text >
</ View >
)
}
function UseResolveClassNamesExample () {
const styles = useResolveClassNames ( 'text-primary' )
return (
< View className = "w-full p-4 bg-base rounded-2xl gap-1" >
< Text className = "text-default font-bold" > useResolveClassNames </ Text >
< Text style = { styles } > Resolved from 'text-primary' </ Text >
</ View >
)
}
function UseCSSVariableExample () {
const primaryColor = useCSSVariable ( '--color-primary' )
return (
< View className = "w-full p-4 bg-base rounded-2xl gap-1" >
< Text className = "text-default font-bold" > useCSSVariable </ Text >
< Text className = "text-primary" > --color-primary: { String ( primaryColor ) } </ Text >
</ View >
)
}
function WithUniwindExample () {
return (
< View className = "w-full p-4 bg-base rounded-2xl gap-2" >
< Text className = "text-default font-bold" > withUniwind </ Text >
< View className = "flex-row gap-3 items-center" >
< StyledFeather name = "star" className = "size-6 text-primary" />
< StyledFeather name = "heart" className = "size-6 text-primary" />
< StyledFeather name = "zap" className = "size-6 text-primary" />
< Text className = "text-primary" > Icons via withUniwind </ Text >
</ View >
</ View >
)
}
function ThemedButton ({ title } : { title : string }) {
return (
< Pressable className = "px-6 py-3 bg-primary rounded-xl active:scale-[0.97] active:opacity-80" >
< Text className = "text-white font-semibold text-center" > { title } </ Text >
</ Pressable >
)
}
export function ScopedThemeHooksExample () {
return (
< View className = "w-full gap-3" >
< ScopedTheme theme = "coffee" >
< View className = "w-full gap-3" >
< ThemedButton title = "Coffee Button" />
< UseUniwindExample />
< UseResolveClassNamesExample />
< UseCSSVariableExample />
< WithUniwindExample />
</ View >
</ ScopedTheme >
< View className = "w-full gap-3" >
< ThemedButton title = "Current Theme Button" />
< UseUniwindExample />
< UseResolveClassNamesExample />
< UseCSSVariableExample />
< WithUniwindExample />
</ View >
</ View >
)
}
API Reference
Component Signature
import { ScopedTheme } from 'uniwind'
< ScopedTheme theme = "dark" >
{ /* themed subtree */ }
</ ScopedTheme >
Props
Theme name to apply to this subtree. The theme must exist in your configured Uniwind theme definitions (for example, light, dark, or a custom theme like coffee).
React children rendered inside the scoped theme boundary.
Behavior Notes
ScopedTheme affects only its descendants
It does not change the global app theme
Nested scopes override parent scopes for their own subtree
Multiple scopes can render side by side on the same screen
Theming Basics Learn how to set up themes in Uniwind
Custom Themes Define custom themes like coffee
useUniwind Read the current theme and adaptive theme state
useCSSVariable Read theme variables directly in JavaScript
useResolveClassNames Resolve class names into style objects at runtime
withUniwind Add className support to third-party components