Skip to content

Setup Guide

This guide covers advanced configuration options for RNC Theme, including custom themes, internationalization, and performance optimizations.

RNC Theme provides a comprehensive theming solution for React Native applications with built-in support for:

Dynamic Theming

Light/dark mode with custom theme support

Internationalization

Multi-language support with auto-detection

Toast System

Configurable toast notifications

⚡ Performance

Optimized with memoization and lazy loading

The RNCProvider is the core component that provides theme context to your entire application. Here are all available configuration options:

_layout.tsx
import { RNCProvider } from 'rnc-theme';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
export default function RootLayout() {
return (
<GestureHandlerRootView>
<RNCProvider defaultTheme="system">
{/* Your app content */}
</RNCProvider>
</GestureHandlerRootView>
);
}
OptionTypeDefaultDescription
defaultTheme'light' | 'dark' | 'system''system'Initial theme mode
toastToastConfig{}Toast notification settings
i18nConfigI18nConfigundefinedInternationalization configuration
bottomSheetPropsBottomSheetProps{}Bottom sheet component props
customLightThemeThemeConfig{}Custom light theme override
customDarkThemeThemeConfig{}Custom dark theme override
  1. Import the required hooks

    import { useTheme, themeRegistry } from 'rnc-theme';
  2. Access theme in your component

    const ThemeSetup = () => {
    const { theme, themeMode, setThemeMode } = useTheme();
    return (
    <View style={{
    backgroundColor: theme.colors.background,
    }}>
    <Text style={{ color: theme.colors.text }}>
    Current theme: {themeMode}
    </Text>
    </View>
    );
    };
  3. Handle theme changes

    const handleThemeChange = (newTheme: 'light' | 'dark' | 'system') => {
    setThemeMode(newTheme);
    };

Register custom themes using the theme registry for advanced customization:

import { themeRegistry, CustomThemeConfigFactory } from 'rnc-theme';
import { useMemo } from 'react';
const customThemeConfig: CustomThemeConfigFactory = useMemo(
() => (isDark: boolean) => ({
colors: {
primary: isDark ? '#FF6B6B' : '#4ECDC4',
secondary: isDark ? '#FFE66D' : '#45B7D1',
background: isDark ? '#1a1a1a' : '#f8f9fa',
surface: isDark ? '#2d2d2d' : '#ffffff',
text: isDark ? '#ffffff' : '#333333',
textSecondary: isDark ? '#b0b0b0' : '#666666',
border: isDark ? '#404040' : '#e0e0e0',
error: '#FF5252',
warning: '#FF9800',
success: '#4CAF50',
info: '#2196F3',
muted: isDark ? '#666666' : '#999999',
accent: isDark ? '#FF6B6B' : '#4ECDC4',
destructive: '#FF5252',
},
components: {
height: {
xs: 32,
sm: 36,
md: 40,
lg: 44,
xl: 48,
},
padding: {
xs: 8,
sm: 12,
md: 16,
lg: 20,
xl: 24,
},
borderRadius: {
xs: 4,
sm: 4,
md: 8,
lg: 16,
xl: 24,
full: 9999,
},
},
spacing: {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
xxl: 48,
},
}),
[]
);
// Register the custom theme
themeRegistry.registerPreset('custom', customThemeConfig);
interface ThemeColors {
primary: string;
secondary: string;
background: string;
surface: string;
text: string;
textSecondary: string;
border: string;
error: string;
warning: string;
success: string;
info: string;
muted: string;
accent: string;
destructive: string;
}
import { RNCProvider } from 'rnc-theme';
import * as Localization from 'expo-localization';
const i18nConfig: I18nConfig = {
translations: {
en: {
title: 'Internationalization Demo',
subtitle: 'Language switching example',
currentLanguage: 'Current Language',
switchLanguage: 'Switch Language',
greeting: 'Hello, World!',
description: 'This is a demonstration of the i18n functionality in the rnc-theme library.',
features: {
title: 'Features',
dynamicSwitching: 'Dynamic language switching',
persistentStorage: 'Persistent language storage',
customConfig: 'Custom configuration support',
autoDetection: 'Auto locale detection',
},
buttons: {
english: 'English',
indonesian: 'Indonesian',
french: 'French',
},
},
id: {
title: 'Demo Internasionalisasi',
subtitle: 'Contoh pergantian bahasa',
currentLanguage: 'Bahasa Saat Ini',
switchLanguage: 'Ganti Bahasa',
greeting: 'Halo, Dunia!',
description: 'Ini adalah demonstrasi fungsionalitas i18n dalam library rnc-theme.',
features: {
title: 'Fitur',
dynamicSwitching: 'Pergantian bahasa dinamis',
persistentStorage: 'Penyimpanan bahasa persisten',
customConfig: 'Dukungan konfigurasi kustom',
autoDetection: 'Deteksi lokal otomatis',
},
buttons: {
english: 'Inggris',
indonesian: 'Indonesia',
french: 'Prancis',
},
},
fr: {
title: "Démo d'Internationalisation",
subtitle: 'Exemple de changement de langue',
currentLanguage: 'Langue Actuelle',
switchLanguage: 'Changer de Langue',
greeting: 'Bonjour le Monde!',
description: 'Ceci est une démonstration de la fonctionnalité i18n dans la bibliothèque rnc-theme.',
features: {
title: 'Fonctionnalités',
dynamicSwitching: 'Changement de langue dynamique',
persistentStorage: 'Stockage de langue persistant',
customConfig: 'Support de configuration personnalisée',
autoDetection: 'Détection automatique des paramètres régionaux',
},
buttons: {
english: 'Anglais',
indonesian: 'Indonésien',
french: 'Français',
},
},
},
supportedLocales: ['en', 'id', 'fr'],
defaultLocale: 'en',
autoDetectLocale: true,
};
const App = () => (
<RNCProvider i18nConfig={i18nConfig}>
{/* Your app content */}
</RNCProvider>
);
import { useLanguage } from 'rnc-theme';
const MyComponent = () => {
const { i18n: { t } } = useLanguage();
return (
<View>
<Text>{t('title')}</Text>
<Text>{t('features.title')}</Text>
</View>
);
};

Configure toast notifications at the provider level:

<RNCProvider
toast={{
maxToasts: 3, // Maximum number of visible toasts
position: "bottom", // 'top' | 'bottom'
}}
>
{/* Your app content */}
</RNCProvider>
import { useToast } from 'rnc-theme';
const MyComponent = () => {
const { toast } = useToast();
const showBasicToast = () => {
toast({
title: 'Success!',
description: 'Your action was completed successfully.',
});
};
const showErrorToast = () => {
toast({
variant: 'error',
title: 'Error occurred',
description: 'Something went wrong. Please try again.',
});
};
return (
<View>
<Button onPress={showBasicToast}>Show Success</Button>
<Button onPress={showErrorToast}>Show Error</Button>
</View>
);
};

Default

Standard informational toast

Success

Success confirmation toast

Warning

Warning or caution toast

Error

Error or failure toast

Implement lazy loading for better performance:

import { lazy, Suspense } from 'react';
import { Spinner } from 'rnc-theme';
// Lazy load heavy components
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const AnotherHeavyComponent = lazy(() => import('./AnotherHeavyComponent'));
const App = () => (
<View>
<Suspense fallback={<Spinner size="lg" />}>
<HeavyComponent />
</Suspense>
<Suspense fallback={<Spinner size="md" />}>
<AnotherHeavyComponent />
</Suspense>
</View>
);
import { memo, useMemo, useCallback } from 'react';
import { useThemedStyles, Theme } from 'rnc-theme';
import { StyleSheet } from 'react-native';
const OptimizedComponent = memo(({ data }) => {
// useThemedStyles includes memoization
const styles = useThemedStyles(createStyles);
// Memoize expensive calculations
const processedData = useMemo(() => {
return data.map(item => ({
...item,
processed: true,
timestamp: Date.now()
}));
}, [data]);
// Memoize callbacks to prevent child re-renders
const handlePress = useCallback((id: string) => {
console.log('Item pressed:', id);
}, []);
const handleLongPress = useCallback((id: string) => {
console.log('Item long pressed:', id);
}, []);
return (
<View style={styles.container}>
{processedData.map(item => (
<TouchableOpacity
key={item.id}
style={styles.item}
onPress={() => handlePress(item.id)}
onLongPress={() => handleLongPress(item.id)}
>
<Text style={styles.text}>{item.name}</Text>
<Text style={styles.subtitle}>{item.description}</Text>
</TouchableOpacity>
))}
</View>
);
});
const createStyles = (theme: Theme) => StyleSheet.create({
container: {
backgroundColor: theme.colors.background,
padding: theme.spacing.md,
flex: 1,
},
item: {
backgroundColor: theme.colors.surface,
padding: theme.spacing.sm,
marginBottom: theme.spacing.xs,
borderRadius: theme.components.borderRadius.md,
borderWidth: 1,
borderColor: theme.colors.border,
},
text: {
color: theme.colors.text,
fontSize: 16,
fontWeight: '600',
},
subtitle: {
color: theme.colors.textSecondary,
fontSize: 14,
marginTop: theme.spacing.xs,
},
});
// Set display name for debugging
OptimizedComponent.displayName = 'OptimizedComponent';
export default OptimizedComponent;
import { Platform } from 'react-native';
const iosSpecificConfig = {
components: {
height: {
xs: Platform.OS === 'ios' ? 34 : 32,
sm: Platform.OS === 'ios' ? 38 : 36,
md: Platform.OS === 'ios' ? 42 : 40,
lg: Platform.OS === 'ios' ? 46 : 44,
xl: Platform.OS === 'ios' ? 50 : 48,
},
borderRadius: {
xs: Platform.OS === 'ios' ? 6 : 4,
sm: Platform.OS === 'ios' ? 8 : 6,
md: Platform.OS === 'ios' ? 10 : 8,
lg: Platform.OS === 'ios' ? 14 : 12,
xl: Platform.OS === 'ios' ? 18 : 16,
},
padding: {
xs: Platform.OS === 'ios' ? 10 : 8,
sm: Platform.OS === 'ios' ? 14 : 12,
md: Platform.OS === 'ios' ? 18 : 16,
lg: Platform.OS === 'ios' ? 22 : 20,
xl: Platform.OS === 'ios' ? 26 : 24,
},
},
};
const androidSpecificConfig = {
colors: {
// Material Design 3 colors
primary: Platform.OS === 'android' ? '#6750A4' : '#007AFF',
secondary: Platform.OS === 'android' ? '#625B71' : '#5AC8FA',
success: Platform.OS === 'android' ? '#4CAF50' : '#34C759',
warning: Platform.OS === 'android' ? '#FF9800' : '#FF9500',
error: Platform.OS === 'android' ? '#F44336' : '#FF3B30',
},
components: {
// Android-specific elevation
elevation: Platform.OS === 'android' ? {
xs: 1,
sm: 2,
md: 4,
lg: 8,
xl: 12,
} : undefined,
// Android ripple effect
ripple: Platform.OS === 'android' ? {
color: 'rgba(0, 0, 0, 0.12)',
borderless: false,
} : undefined,
},
};

Organize your theme-related files for better maintainability:

  • Directorysrc
    • Directorythemes/
      • index.ts # Main theme exports
      • presets.ts # Custom theme presets
      • colors.ts # Color definitions
      • tokens.ts # Design tokens

Always prefer theme tokens over hardcoded values:

const createStyles = () => StyleSheet.create({
container: {
backgroundColor: '#FFFFFF', // Hardcoded color
padding: 16, // Hardcoded spacing
borderRadius: 12, // Hardcoded radius
borderWidth: 1,
borderColor: '#E0E0E0', // Hardcoded border
},
text: {
color: '#333333', // Hardcoded text color
fontSize: 16,
},
});
  1. Use memoization for expensive computations

    const expensiveValue = useMemo(() => computeExpensiveValue(data), [data]);
  2. Memoize callbacks to prevent unnecessary re-renders

    const handlePress = useCallback(() => {
    // Handle press
    }, [dependency]);
  3. Use useThemedStyles for dynamic styling

    const styles = useThemedStyles(createStyles); // Auto-memoized
  4. Implement lazy loading for heavy components

    const HeavyComponent = lazy(() => import('./HeavyComponent'));

Ensure your themed components are accessible:

const createAccessibleStyles = (theme: Theme) => StyleSheet.create({
button: {
backgroundColor: theme.colors.primary,
padding: theme.spacing.md,
borderRadius: theme.components.borderRadius.md,
minHeight: 44, // Minimum touch target size
},
buttonText: {
color: theme.colors.surface,
textAlign: 'center',
fontSize: 16,
fontWeight: '600',
},
});
const AccessibleButton = ({ onPress, children }) => {
const styles = useThemedStyles(createAccessibleStyles);
return (
<TouchableOpacity
style={styles.button}
onPress={onPress}
accessible={true}
accessibilityRole="button"
accessibilityLabel={typeof children === 'string' ? children : 'Button'}
>
<Text style={styles.buttonText}>{children}</Text>
</TouchableOpacity>
);
};

Theme Not Persisting

Problem: Theme changes don’t persist between app restarts Solution: Check AsyncStorage permissions and ensure the provider is at the root level

// Ensure RNCProvider wraps your entire app
<RNCProvider>
<App />
</RNCProvider>

Styles Not Updating

Problem: Component styles don’t update when theme changes Solution: Use useThemedStyles instead of static styles

// ❌ Static styles won't update
const styles = StyleSheet.create({...});
// ✅ Dynamic styles update with theme
const styles = useThemedStyles(createStyles);

Performance Issues

Problem: App feels sluggish with theme changes Solution: Implement proper memoization and lazy loading

// Memoize expensive operations
const memoizedStyles = useThemedStyles(createStyles);
const memoizedData = useMemo(() => processData(data), [data]);

TypeScript Errors

Problem: TypeScript complains about theme types Solution: Update type definitions and use proper imports

import { Theme } from 'rnc-theme';
const createStyles = (theme: Theme) => StyleSheet.create({
// Your styles
});

Monitor theme performance with React DevTools Profiler: