React Native Theme TransitionReact Native Theme Transition
Recipes

Haptic Feedback & Analytics

Add haptic feedback and analytics tracking to theme transitions.

Haptic feedback

On every animated transition (config-level)

import * as Haptics from 'expo-haptics';

export const { ThemeTransitionProvider, useTheme } = createThemeTransition({
  themes: { light, dark },
  onTransitionStart: () => {
    Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
  },
});

On a specific button only (per-call)

function ThemeToggle() {
  const { name, setTheme } = useTheme();

  return (
    <Pressable onPress={() => {
      setTheme(name === 'light' ? 'dark' : 'light', {
        onTransitionStart: () => {
          Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
        },
      });
    }}>
      <Text>Toggle</Text>
    </Pressable>
  );
}

Analytics

Use onThemeChange to track every theme change (animated, instant, system-driven):

export const { ThemeTransitionProvider, useTheme } = createThemeTransition({
  themes: { light, dark },
  onThemeChange: (name) => {
    analytics.track('theme_changed', { theme: name });
  },
});

onThemeChange is the only callback guaranteed to fire for every theme change. onTransitionStart and onTransitionEnd only fire for animated transitions.

Disable UI during transitions

Disable a button

<Pressable
  onPress={() => setTheme('dark')}
  disabled={isTransitioning}
>
  <Text>Switch theme</Text>
</Pressable>

Do not change styles based on isTransitioning (e.g., opacity). The screenshot captures the current visual state — style changes will bleed into the cross-fade.

Defer expensive renders

function HeavyChart() {
  const { isTransitioning, colors } = useTheme();

  if (isTransitioning) {
    return <View style={{ height: 200, backgroundColor: colors.card }} />;
  }

  return <ExpensiveChartComponent colors={colors} />;
}

On this page