Skip to main content

useAppTheme

React hook that provides access to the current theme values throughout your application. This hook is the primary way to access colors, spacing, typography, and other theme properties in your components.

Quick Example

import React from "react";
import { StyleSheet, View } from "react-native";
import { useAppTheme } from "@ovok/mobile";
import { Text } from "react-native-paper";

// Static styles outside component
const styles = StyleSheet.create({
container: {
flex: 1,
},
card: {
borderWidth: 1,
borderRadius: 8,
},
});

export const ThemedComponent = () => {
const theme = useAppTheme();

return (
<View
style={[
styles.container,
{
backgroundColor: theme.colors.background,
padding: theme.spacing(4),
},
]}
>
<View
style={[
styles.card,
{
backgroundColor: theme.colors.surface,
borderColor: theme.colors.outline,
padding: theme.spacing(3),
borderRadius: theme.borderRadius(2),
},
]}
>
<Text
variant="titleMedium"
style={{
color: theme.colors.primary,
marginBottom: theme.spacing(2),
}}
>
Healthcare Dashboard
</Text>
<Text style={{ color: theme.colors.onSurface }}>
Patient vital signs monitoring
</Text>
</View>
</View>
);
};

Hook Signature

const theme: AppTheme = useAppTheme();

Returns

The hook returns an AppTheme object containing:

PropertyTypeDescription
colorsAppTheme["colors"]Complete color palette
spacing(value: number) => numberSpacing function with multiplier
borderRadius(value: number) => numberBorder radius function with multiplier
roundnessnumberBase border radius value
darkbooleanWhether dark mode is enabled

Plus all properties from React Native Paper's MD3Theme.

Theme Properties

Colors

Access to complete color system including Material Design 3 colors and custom healthcare colors:

const theme = useAppTheme();

// Material Design 3 colors
theme.colors.primary; // Primary brand color
theme.colors.secondary; // Secondary brand color
theme.colors.tertiary; // Tertiary brand color
theme.colors.surface; // Surface background
theme.colors.background; // Main background
theme.colors.onSurface; // Text on surface
theme.colors.onBackground; // Text on background

// Healthcare semantic colors
theme.colors.success; // Success/healthy states
theme.colors.error; // Error/critical states
theme.colors.warning; // Warning/attention states

// Extended color palettes
theme.colors.blue[600]; // Specific blue shade
theme.colors.green[500]; // Specific green shade
theme.colors.red[700]; // Specific red shade
// ... and more color families

Spacing Function

Generate consistent spacing values using the configured multiplier:

const theme = useAppTheme();

// Usage patterns
<View
style={{
padding: theme.spacing(4), // 16px (with default 4px multiplier)
marginBottom: theme.spacing(3), // 12px
gap: theme.spacing(2), // 8px
}}
>

Border Radius Function

Generate consistent border radius values:

const theme = useAppTheme();

// Usage patterns
<View
style={{
borderRadius: theme.borderRadius(2), // 8px (with default 4px multiplier)
borderRadius: theme.borderRadius(3), // 12px
borderRadius: theme.borderRadius(4), // 16px
}}
>

Usage Patterns

Component Styling

import React from "react";
import { StyleSheet, View } from "react-native";
import { useAppTheme } from "@ovok/mobile";
import { Text, Button } from "react-native-paper";

// Static styles outside component - performance optimization
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
card: {
borderWidth: 1,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
});

export const StyledCard = ({ title, children, onPress }) => {
const theme = useAppTheme();

return (
<View
style={[
styles.card,
{
backgroundColor: theme.colors.surface,
borderColor: theme.colors.outline,
shadowColor: theme.colors.shadow,
padding: theme.spacing(4),
borderRadius: theme.borderRadius(3),
marginBottom: theme.spacing(3),
},
]}
>
<Text
variant="titleLarge"
style={{
color: theme.colors.onSurface,
marginBottom: theme.spacing(3),
}}
>
{title}
</Text>

{children}

<Button
mode="contained"
onPress={onPress}
style={{
marginTop: theme.spacing(3),
borderRadius: theme.borderRadius(2),
}}
buttonColor={theme.colors.primary}
>
Action
</Button>
</View>
);
};

Healthcare Status Colors

import React from "react";
import { useAppTheme } from "@ovok/mobile";
import { Text } from "react-native-paper";

export const VitalSignDisplay = ({ value, status, unit }) => {
const theme = useAppTheme();

const getStatusColor = (status: string) => {
switch (status) {
case "normal":
return theme.colors.success;
case "warning":
return theme.colors.warning;
case "critical":
return theme.colors.error;
default:
return theme.colors.onSurface;
}
};

return (
<Text
variant="headlineMedium"
style={{
color: getStatusColor(status),
fontWeight: "bold",
}}
>
{value} {unit}
</Text>
);
};

Dynamic Color Palettes

import React from "react";
import { useAppTheme } from "@ovok/mobile";

export const PaletteDisplay = ({ colorFamily }) => {
const theme = useAppTheme();

const colorShades = [
25, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
];

return (
<View style={{ flexDirection: "row", flexWrap: "wrap" }}>
{colorShades.map((shade) => (
<View
key={shade}
style={{
width: 50,
height: 50,
backgroundColor: theme.colors[colorFamily][shade],
margin: theme.spacing(1),
borderRadius: theme.borderRadius(1),
}}
/>
))}
</View>
);
};

Responsive Layout

import React from "react";
import { useAppTheme } from "@ovok/mobile";
import { useWindowDimensions } from "react-native";

export const ResponsiveGrid = ({ children }) => {
const theme = useAppTheme();
const { width } = useWindowDimensions();

const isTablet = width > 768;
const columns = isTablet ? 3 : 2;
const spacing = theme.spacing(isTablet ? 4 : 3);

return (
<View
style={{
flexDirection: "row",
flexWrap: "wrap",
padding: spacing,
gap: spacing,
}}
>
{children}
</View>
);
};

Performance Optimization

Memoizing Theme-Dependent Values

For expensive calculations, memoize theme-dependent values:

import React from "react";
import { useAppTheme } from "@ovok/mobile";

export const ComplexComponent = () => {
const theme = useAppTheme();

// Memoize expensive style calculations
const complexStyles = React.useMemo(
() => ({
shadow: {
shadowColor: theme.colors.shadow,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.15,
shadowRadius: 8,
elevation: 6,
},
gradient: {
// Complex gradient calculations
backgroundColor: theme.colors.surface,
borderColor: theme.colors.outline,
},
}),
[theme.colors.shadow, theme.colors.surface, theme.colors.outline],
);

return (
<View style={[complexStyles.shadow, complexStyles.gradient]}>
{/* Component content */}
</View>
);
};

Avoiding Unnecessary Re-renders

import React from "react";
import { useAppTheme } from "@ovok/mobile";

// ✅ Good - Extract stable parts
const StaticChild = React.memo(() => {
return <Text>This doesn't need theme access</Text>;
});

export const OptimizedComponent = () => {
const theme = useAppTheme();

// ✅ Good - Only theme-dependent parts access theme
return (
<View style={{ backgroundColor: theme.colors.background }}>
<StaticChild />
<Text style={{ color: theme.colors.primary }}>
This needs theme colors
</Text>
</View>
);
};

Common Patterns

Theme-Aware Icon Colors

import React from "react";
import { useAppTheme } from "@ovok/mobile";
import { IconButton } from "react-native-paper";

export const ThemedIconButton = ({ icon, onPress, variant = "primary" }) => {
const theme = useAppTheme();

const iconColor = {
primary: theme.colors.primary,
success: theme.colors.success,
error: theme.colors.error,
surface: theme.colors.onSurface,
}[variant];

return (
<IconButton
icon={icon}
iconColor={iconColor}
onPress={onPress}
style={{
borderRadius: theme.borderRadius(2),
}}
/>
);
};

Conditional Dark Mode Styling

import React from "react";
import { useAppTheme } from "@ovok/mobile";

export const DarkModeAwareComponent = () => {
const theme = useAppTheme();

return (
<View
style={{
backgroundColor: theme.dark
? theme.colors.surface
: theme.colors.background,
borderColor: theme.dark
? theme.colors.outline
: theme.colors.outlineVariant,
borderWidth: 1,
padding: theme.spacing(4),
borderRadius: theme.borderRadius(2),
}}
>
<Text style={{ color: theme.colors.onSurface }}>
Content adapts to dark/light mode
</Text>
</View>
);
};

Form Styling

import React from "react";
import { useAppTheme } from "@ovok/mobile";
import { TextInput } from "react-native-paper";

export const ThemedForm = () => {
const theme = useAppTheme();

return (
<View
style={{
padding: theme.spacing(4),
gap: theme.spacing(3),
}}
>
<TextInput
label="Patient Name"
mode="outlined"
style={{
backgroundColor: theme.colors.surface,
}}
theme={{
colors: {
primary: theme.colors.primary,
outline: theme.colors.outline,
},
}}
/>

<TextInput
label="Medical Record Number"
mode="outlined"
style={{
backgroundColor: theme.colors.surface,
}}
/>
</View>
);
};

TypeScript Integration

The hook is fully typed and provides excellent IntelliSense support:

import { useAppTheme } from "@ovok/mobile";

export const TypedComponent = () => {
const theme = useAppTheme(); // Type: AppTheme

// Full type safety and autocompletion
theme.colors.primary; // string
theme.spacing(4); // number
theme.borderRadius(2); // number
theme.dark; // boolean

// Extended color palettes with type safety
theme.colors.blue[600]; // string
theme.colors.green[500]; // string
};
  • ThemeProvider - Provider component that supplies theme context
  • Colors - Color system and palette definitions