useColorScheme

A cross-platform hook that provides access to the user's preferred color scheme with hydration-safe web support.

Installation

pnpm dlx bna-ui add useColorScheme

Usage

import { useColorScheme } from '@/hooks/useColorScheme';
export function ThemedComponent() {
  const colorScheme = useColorScheme();
 
  return (
    <View
      style={{
        backgroundColor: colorScheme === 'dark' ? '#000' : '#fff',
      }}
    >
      {/* Your themed content */}
    </View>
  );
}

API Reference

useColorScheme

Returns the user's preferred color scheme with platform-specific optimizations.

Returns

TypeDescription
'light' | 'dark' | nullThe current color scheme. Returns null if the color scheme cannot be determined initially.

Platform Behavior

Native (iOS/Android)

  • Uses React Native's built-in useColorScheme hook
  • Automatically updates when the system color scheme changes
  • Returns the actual system preference immediately
  • Listens for system-level theme changes

Web

  • Implements hydration-safe color scheme detection
  • Returns 'light' during server-side rendering to prevent hydration mismatches
  • Updates to the actual system preference after client-side hydration
  • Listens for system color scheme changes via media queries

Implementation Details

The hook uses platform-specific files to handle different environments:

  • useColorScheme.ts - Default implementation for React Native
  • useColorScheme.web.ts - Web-specific implementation with hydration safety

Web Hydration Safety

The web implementation includes a hydration check to prevent mismatches between server and client rendering:

const [hasHydrated, setHasHydrated] = useState(false);
 
useEffect(() => {
  setHasHydrated(true);
}, []);
 
if (hasHydrated) {
  return colorScheme;
}
 
return 'light'; // Safe default during SSR

Use Cases

This hook is essential for:

  • Implementing dark mode support in your applications
  • Creating theme-aware components
  • Adapting UI colors based on system preferences
  • Ensuring consistent theming across platforms
  • Building accessible applications with proper contrast ratios

Best Practices

Handling Null Values

Always handle the null case, especially during initial renders:

const colorScheme = useColorScheme();
const isDark = colorScheme === 'dark';
const backgroundColor = isDark ? '#000' : '#fff';

Theme Provider Pattern

Create a centralized theme provider for consistent theming:

import { useColorScheme } from '@/hooks/useColorScheme';
import { createContext, useContext } from 'react';
 
const ThemeContext = createContext<'light' | 'dark'>('light');
 
export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const colorScheme = useColorScheme();
 
  return (
    <ThemeContext.Provider value={colorScheme ?? 'light'}>
      {children}
    </ThemeContext.Provider>
  );
}
 
export function useTheme() {
  return useContext(ThemeContext);
}

Color Palette Management

Define comprehensive color palettes for each theme:

const colors = {
  light: {
    background: '#ffffff',
    text: '#000000',
    primary: '#007AFF',
    secondary: '#8E8E93',
    border: '#E5E5E7',
  },
  dark: {
    background: '#000000',
    text: '#ffffff',
    primary: '#0A84FF',
    secondary: '#636366',
    border: '#38383A',
  },
};
 
export function useThemeColors() {
  const colorScheme = useColorScheme();
  return colors[colorScheme ?? 'light'];
}

Performance Optimization

Memoize theme-dependent calculations to avoid unnecessary re-renders:

import { useMemo } from 'react';
 
export function useThemedStyles() {
  const colorScheme = useColorScheme();
 
  return useMemo(
    () => ({
      container: {
        backgroundColor: colorScheme === 'dark' ? '#000' : '#fff',
        flex: 1,
      },
      text: {
        color: colorScheme === 'dark' ? '#fff' : '#000',
      },
    }),
    [colorScheme]
  );
}

Testing Both Modes

Always test your application in both light and dark modes:

// Test component
export function ThemeTestComponent() {
  const colorScheme = useColorScheme();
 
  return (
    <View style={{ padding: 20 }}>
      <Text>Current theme: {colorScheme}</Text>
      <Text>Test your components in both modes!</Text>
    </View>
  );
}

Advanced Usage

Custom Theme Hook

Create a more sophisticated theme hook with additional features:

import { useColorScheme } from '@/hooks/useColorScheme';
import { useMemo } from 'react';
 
interface Theme {
  colors: {
    primary: string;
    secondary: string;
    background: string;
    text: string;
    border: string;
  };
  spacing: {
    xs: number;
    sm: number;
    md: number;
    lg: number;
    xl: number;
  };
  borderRadius: {
    sm: number;
    md: number;
    lg: number;
  };
}
 
export function useTheme(): Theme {
  const colorScheme = useColorScheme();
 
  return useMemo(() => {
    const isDark = colorScheme === 'dark';
 
    return {
      colors: {
        primary: isDark ? '#0A84FF' : '#007AFF',
        secondary: isDark ? '#636366' : '#8E8E93',
        background: isDark ? '#000000' : '#FFFFFF',
        text: isDark ? '#FFFFFF' : '#000000',
        border: isDark ? '#38383A' : '#E5E5E7',
      },
      spacing: {
        xs: 4,
        sm: 8,
        md: 16,
        lg: 24,
        xl: 32,
      },
      borderRadius: {
        sm: 4,
        md: 8,
        lg: 12,
      },
    };
  }, [colorScheme]);
}

Dependencies

  • react-native - Required for the base useColorScheme hook
  • react - Required for the web implementation hooks (useState, useEffect)

Accessibility

The hook helps maintain proper accessibility by:

  • Respecting user's system-level accessibility preferences
  • Supporting high contrast modes automatically
  • Enabling proper color contrast ratios for different themes
  • Ensuring consistent theming across the application