Skip to main content
Expo Router uses a file-based routing system. To show toasts across all screens, you need to add the <BreadLoaf /> component to your root layout file.

Basic setup

Add <BreadLoaf /> to your root layout file (app/_layout.tsx):
app/_layout.tsx
import { Stack } from 'expo-router';
import { BreadLoaf } from 'react-native-bread';

export default function RootLayout() {
  return (
    <>
      <Stack>
        <Stack.Screen name="index" />
      </Stack>
      <BreadLoaf />
    </>
  );
}
That’s it! You can now show toasts from any screen:
app/index.tsx
import { View, Button } from 'react-native';
import { toast } from 'react-native-bread';

export default function HomeScreen() {
  return (
    <View>
      <Button
        title="Show Toast"
        onPress={() => toast.success('Hello from Expo Router!')}
      />
    </View>
  );
}

With tabs navigator

For apps using tabs, place <BreadLoaf /> in the root layout, not the tabs layout:
app/_layout.tsx
import { Stack } from 'expo-router';
import { BreadLoaf } from 'react-native-bread';

export default function RootLayout() {
  return (
    <>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      </Stack>
      <BreadLoaf />
    </>
  );
}
app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';

export default function TabLayout() {
  return (
    <Tabs>
      <Tabs.Screen name="index" options={{ title: 'Home' }} />
      <Tabs.Screen name="profile" options={{ title: 'Profile' }} />
      <Tabs.Screen name="settings" options={{ title: 'Settings' }} />
    </Tabs>
  );
}
Place <BreadLoaf /> in your root layout (usually app/_layout.tsx), not in nested layouts. This ensures toasts appear consistently across all screens.

With modals

Expo Router supports modal presentation. Add <BreadLoaf /> to the root and configure modal screens:
app/_layout.tsx
import { Stack } from 'expo-router';
import { BreadLoaf } from 'react-native-bread';
import { Platform } from 'react-native';

export default function RootLayout() {
  return (
    <>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
        <Stack.Screen 
          name="modal" 
          options={{ 
            presentation: Platform.OS === 'android' ? 'containedModal' : 'modal',
            title: 'Modal Screen',
          }} 
        />
      </Stack>
      <BreadLoaf />
    </>
  );
}
app/modal.tsx
import { View, Button, StyleSheet } from 'react-native';
import { toast } from 'react-native-bread';
import { router } from 'expo-router';

export default function ModalScreen() {
  return (
    <View style={styles.container}>
      <Button
        title="Show Toast"
        onPress={() => toast.success('Toast in modal!')}
      />
      <Button title="Close" onPress={() => router.back()} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    gap: 16,
  },
});
For Android native modals, see the Modal setup guide.

With configuration

Customize toast behavior via the config prop:
app/_layout.tsx
import { Stack } from 'expo-router';
import { BreadLoaf } from 'react-native-bread';

export default function RootLayout() {
  return (
    <>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      </Stack>
      <BreadLoaf
        config={{
          position: 'bottom',
          defaultDuration: 5000,
          maxStack: 3,
          colors: {
            success: {
              accent: '#22c55e',
              background: '#f0fdf4',
            },
            error: {
              accent: '#ef4444',
              background: '#fef2f2',
            },
          },
        }}
      />
    </>
  );
}

Complete example

Here’s a full Expo Router app structure with tabs, modals, and toasts:
app/_layout.tsx
import { Stack } from 'expo-router';
import { BreadLoaf } from 'react-native-bread';
import { Platform } from 'react-native';

export default function RootLayout() {
  return (
    <>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
        <Stack.Screen 
          name="(modal)" 
          options={{ 
            presentation: Platform.OS === 'android' ? 'containedModal' : 'modal'
          }} 
        />
        <Stack.Screen name="+not-found" />
      </Stack>
      <BreadLoaf
        config={{
          position: 'top',
          stacking: true,
          maxStack: 3,
          defaultDuration: 4000,
        }}
      />
    </>
  );
}
app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { IconSymbol } from '@/components/ui/IconSymbol';

export default function TabLayout() {
  return (
    <Tabs screenOptions={{ headerShown: false }}>
      <Tabs.Screen
        name="index"
        options={{
          title: 'Home',
          tabBarIcon: ({ color }) => <IconSymbol name="house.fill" color={color} />,
        }}
      />
      <Tabs.Screen
        name="explore"
        options={{
          title: 'Explore',
          tabBarIcon: ({ color }) => <IconSymbol name="paperplane.fill" color={color} />,
        }}
      />
    </Tabs>
  );
}
app/(tabs)/index.tsx
import { View, Button, StyleSheet } from 'react-native';
import { toast } from 'react-native-bread';
import { router } from 'expo-router';

export default function HomeScreen() {
  return (
    <View style={styles.container}>
      <Button
        title="Success Toast"
        onPress={() => toast.success('Success!', 'Operation completed')}
      />
      <Button
        title="Error Toast"
        onPress={() => toast.error('Error!', 'Something went wrong')}
      />
      <Button
        title="Promise Toast"
        onPress={async () => {
          await toast.promise(
            fetch('/api/data').then(r => r.json()),
            {
              loading: 'Loading data...',
              success: 'Data loaded!',
              error: 'Failed to load data',
            }
          );
        }}
      />
      <Button
        title="Open Modal"
        onPress={() => router.push('/modal')}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    gap: 16,
  },
});
app/(tabs)/explore.tsx
import { View, Text, Button, StyleSheet } from 'react-native';
import { toast } from 'react-native-bread';

export default function ExploreScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Explore Screen</Text>
      <Button
        title="Custom Toast"
        onPress={() => {
          toast.custom(
            ({ dismiss }) => (
              <View style={styles.customToast}>
                <Text style={styles.customText}>Custom notification!</Text>
                <Button title="Dismiss" onPress={dismiss} />
              </View>
            ),
            { duration: 5000 }
          );
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    gap: 16,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 16,
  },
  customToast: {
    padding: 16,
    backgroundColor: '#fff',
    borderRadius: 12,
    flexDirection: 'row',
    alignItems: 'center',
    gap: 12,
  },
  customText: {
    flex: 1,
    fontSize: 16,
  },
});
app/(modal)/_layout.tsx
import { Stack } from 'expo-router';
import { ToastPortal } from 'react-native-bread';

export default function ModalLayout() {
  return (
    <>
      <Stack>
        <Stack.Screen name="index" options={{ title: 'Modal' }} />
      </Stack>
      <ToastPortal />
    </>
  );
}
app/(modal)/index.tsx
import { View, Button, Text, StyleSheet } from 'react-native';
import { toast } from 'react-native-bread';
import { router } from 'expo-router';

export default function ModalScreen() {
  const handleSave = async () => {
    const result = await toast.promise(
      saveData(),
      {
        loading: 'Saving...',
        success: 'Saved successfully!',
        error: 'Failed to save',
      }
    );

    if (result.success) {
      router.back();
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Modal Screen</Text>
      <Button title="Save" onPress={handleSave} />
      <Button title="Cancel" onPress={() => router.back()} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    gap: 16,
    padding: 16,
  },
  title: {
    fontSize: 20,
    fontWeight: '600',
    marginBottom: 24,
  },
});

async function saveData() {
  // Simulate API call
  await new Promise(resolve => setTimeout(resolve, 2000));
  return { success: true };
}

Common patterns

Troubleshooting

Make sure <BreadLoaf /> is placed in your root layout (app/_layout.tsx), not in a nested layout or screen file.
// ✅ Correct
// app/_layout.tsx
export default function RootLayout() {
  return (
    <>
      <Stack />
      <BreadLoaf />
    </>
  );
}

// ❌ Incorrect
// app/(tabs)/_layout.tsx
export default function TabLayout() {
  return (
    <>
      <Tabs />
      <BreadLoaf /> {/* Too nested */}
    </>
  );
}
Use containedModal presentation or add ToastPortal to your modal layout. See the Modal setup guide for details.
// Option 1: containedModal
<Stack.Screen 
  name="modal" 
  options={{ presentation: Platform.OS === 'android' ? 'containedModal' : 'modal' }} 
/>

// Option 2: ToastPortal in modal layout
// app/(modal)/_layout.tsx
export default function ModalLayout() {
  return (
    <>
      <Stack />
      <ToastPortal />
    </>
  );
}
Toast configuration is global. Make sure you’re only setting config once in your root layout:
// ✅ Configure once
// app/_layout.tsx
<BreadLoaf config={{ position: 'bottom' }} />

// ❌ Don't configure in multiple places
// app/(tabs)/_layout.tsx
<BreadLoaf config={{ position: 'top' }} /> {/* Don't do this */}

Next steps

Modal setup

Configure toasts for modal screens on Android

Custom toasts

Create fully custom toast designs

Promise toasts

Handle async operations with automatic state transitions

RTL support

Configure right-to-left layout support

Build docs developers (and LLMs) love