Skip to content

useThemedStyles Hook

The useThemedStyles hook provides a performant way to create theme-aware styles in React Native applications. It automatically memoizes style objects and recreates them only when the theme changes.

Basic Usage Example
import { useThemedStyles } from 'your-theme-package';
function MyComponent() {
const styles = useThemedStyles((theme) => ({
container: {
backgroundColor: theme.colors.background,
padding: theme.spacing.md,
},
title: {
color: theme.colors.text,
fontSize: theme.fontSizes.lg,
fontFamily: theme.typography.fontFamily.bold,
},
button: {
backgroundColor: theme.colors.primary,
borderRadius: theme.components.borderRadius.md,
padding: theme.spacing.sm,
},
}));
return (
<View style={styles.container}>
<Text style={styles.title}>Hello World</Text>
<TouchableOpacity style={styles.button}>
<Text>Press Me</Text>
</TouchableOpacity>
</View>
);
}

Performance

Uses useMemo to prevent unnecessary style recalculations, improving component performance.

Type Safe

Full TypeScript support with generic constraints ensuring type safety for style objects.

Theme Reactive

Automatically updates styles when theme changes, ensuring consistent UI across theme switches.

RN Compatible

Works seamlessly with React Native’s StyleSheet API and component styling patterns.

ComplexCard.tsx
function ComplexCard({ title, subtitle, onPress }) {
const styles = useThemedStyles((theme) => ({
card: {
backgroundColor: theme.colors.surface,
borderRadius: theme.components.borderRadius.lg,
padding: theme.spacing.lg,
marginVertical: theme.spacing.sm,
shadowColor: theme.colors.shadow,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: theme.spacing.md,
},
title: {
fontSize: theme.fontSizes.xl,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.text,
},
subtitle: {
fontSize: theme.fontSizes.sm,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginTop: theme.spacing.xs,
},
button: {
backgroundColor: theme.colors.primary,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.sm,
borderRadius: theme.components.borderRadius.sm,
},
buttonText: {
color: theme.colors.onPrimary,
fontSize: theme.fontSizes.sm,
fontFamily: theme.typography.fontFamily.medium,
},
}));
return (
<View style={styles.card}>
<View style={styles.header}>
<View>
<Text style={styles.title}>{title}</Text>
<Text style={styles.subtitle}>{subtitle}</Text>
</View>
<TouchableOpacity style={styles.button} onPress={onPress}>
<Text style={styles.buttonText}>Action</Text>
</TouchableOpacity>
</View>
</View>
);
}
ConditionalButton.tsx
function ConditionalButton({ variant = 'primary', disabled = false, children }) {
const styles = useThemedStyles((theme) => ({
button: {
paddingHorizontal: theme.spacing.lg,
paddingVertical: theme.spacing.md,
borderRadius: theme.components.borderRadius.md,
alignItems: 'center',
justifyContent: 'center',
},
primary: {
backgroundColor: disabled
? theme.colors.disabled
: theme.colors.primary,
},
secondary: {
backgroundColor: disabled
? theme.colors.disabled
: theme.colors.secondary,
borderWidth: 1,
borderColor: theme.colors.border,
},
outline: {
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: disabled
? theme.colors.disabled
: theme.colors.primary,
},
text: {
fontSize: theme.fontSizes.md,
fontFamily: theme.typography.fontFamily.medium,
},
primaryText: {
color: disabled
? theme.colors.textDisabled
: theme.colors.onPrimary,
},
secondaryText: {
color: disabled
? theme.colors.textDisabled
: theme.colors.onSecondary,
},
outlineText: {
color: disabled
? theme.colors.textDisabled
: theme.colors.primary,
},
}));
return (
<TouchableOpacity
style={[styles.button, styles[variant]]}
disabled={disabled}
>
<Text style={[styles.text, styles[`${variant}Text`]]}>
{children}
</Text>
</TouchableOpacity>
);
}
ProgressBar.tsx
interface ProgressBarProps {
progress: number; // 0-100
height?: number;
showLabel?: boolean;
}
function ProgressBar({ progress, height = 8, showLabel = true }: ProgressBarProps) {
const styles = useThemedStyles((theme) => ({
container: {
width: '100%',
marginVertical: theme.spacing.sm,
},
track: {
height,
backgroundColor: theme.colors.surfaceVariant,
borderRadius: height / 2,
overflow: 'hidden',
},
fill: {
height: '100%',
backgroundColor: theme.colors.primary,
borderRadius: height / 2,
width: `${Math.min(Math.max(progress, 0), 100)}%`,
},
label: {
fontSize: theme.fontSizes.sm,
color: theme.colors.textSecondary,
textAlign: 'center',
marginTop: theme.spacing.xs,
},
}));
return (
<View style={styles.container}>
<View style={styles.track}>
<View style={styles.fill} />
</View>
{showLabel && (
<Text style={styles.label}>
{Math.round(progress)}%
</Text>
)}
</View>
);
}
// ✅ Style factory defined outside component or memoized
const createStyles = (theme) => ({
container: {
backgroundColor: theme.colors.background,
padding: theme.spacing.md,
},
});
function OptimizedComponent() {
const styles = useThemedStyles(createStyles);
return <View style={styles.container} />;
}
Optimized Dynamic Styles
// ✅ Use useCallback for dynamic style factories
function DynamicComponent({ isActive }) {
const createStyles = useCallback((theme) => ({
container: {
backgroundColor: isActive
? theme.colors.primary
: theme.colors.surface,
opacity: isActive ? 1 : 0.6,
},
}), [isActive]);
const styles = useThemedStyles(createStyles);
return <View style={styles.container} />;
}
TypedButton.tsx
import { StyleSheet } from 'react-native';
import { Theme } from '../types/theme';
// Define your style types
interface ButtonStyles {
container: object;
text: object;
disabled: object;
}
function TypedButton() {
// Fully typed style factory
const styles = useThemedStyles<ButtonStyles>((theme: Theme) => ({
container: {
backgroundColor: theme.colors.primary,
borderRadius: theme.components.borderRadius.md,
padding: theme.spacing.md,
},
text: {
color: theme.colors.onPrimary,
fontSize: theme.fontSizes.md,
},
disabled: {
backgroundColor: theme.colors.disabled,
color: theme.colors.textDisabled,
},
}));
return (
<TouchableOpacity style={styles.container}>
<Text style={styles.text}>Button</Text>
</TouchableOpacity>
);
}
Custom Theme Extension
// Extend the theme type for custom properties
interface ExtendedTheme extends Theme {
customColors: {
brand: string;
accent: string;
};
}
function ComponentWithCustomTheme() {
const styles = useThemedStyles((theme: ExtendedTheme) => ({
container: {
backgroundColor: theme.customColors.brand,
borderColor: theme.customColors.accent,
},
}));
return <View style={styles.container} />;
}
  1. Define style factories outside components when possible to prevent unnecessary re-renders

  2. Use useCallback for dynamic style factories that depend on props or state

  3. Leverage TypeScript for better development experience and catch styling errors early

  4. Keep style objects flat to maximize StyleSheet optimization benefits

  5. Use semantic theme tokens rather than hardcoded values for better maintainability

  6. Test theme switching to ensure styles update correctly across different themes

ResponsiveCard.tsx
import { Dimensions } from 'react-native';
function ResponsiveCard() {
const { width } = Dimensions.get('window');
const styles = useThemedStyles((theme) => ({
card: {
backgroundColor: theme.colors.surface,
borderRadius: theme.components.borderRadius.lg,
padding: width > 768 ? theme.spacing.xl : theme.spacing.md,
marginHorizontal: width > 768 ? theme.spacing.lg : theme.spacing.sm,
},
title: {
fontSize: width > 768 ? theme.fontSizes.xxl : theme.fontSizes.xl,
color: theme.colors.text,
},
}));
return (
<View style={styles.card}>
<Text style={styles.title}>Responsive Card</Text>
</View>
);
}
PlatformAwareComponent.tsx
import { Platform } from 'react-native';
function PlatformAwareComponent() {
const styles = useThemedStyles((theme) => ({
container: {
backgroundColor: theme.colors.background,
padding: theme.spacing.md,
...Platform.select({
ios: {
shadowColor: theme.colors.shadow,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
android: {
elevation: 4,
},
}),
},
text: {
fontSize: theme.fontSizes.md,
fontFamily: Platform.select({
ios: theme.typography.fontFamily.regular,
android: theme.typography.fontFamily.medium,
}),
color: theme.colors.text,
},
}));
return (
<View style={styles.container}>
<Text style={styles.text}>Platform Aware Text</Text>
</View>
);
}

Styles not updating on theme change

Cause: Style factory function reference changing on every render

Solution: Define style factory outside component or wrap with useCallback

Performance issues with complex styles

Cause: Heavy computations in style factory

Solution: Optimize style calculations or consider breaking into smaller components

TypeScript errors with style properties

Cause: Incorrect theme type or style property types

Solution: Ensure theme type matches your theme structure and use proper React Native style types