Skip to main content

Adding New Screens to the Mobile App

This guide walks you through adding new screens to the Launch mobile app, including proper navigation setup, route registration, and navigation guards.

Overview

The Launch mobile app uses a nested navigation structure with NativeTabs inside an AppStack:
AppStack (main navigation)
├── Authentication routes (/auth/*)
├── Onboarding routes (/onboarding/*)
├── (tabs) ← NativeTabs navigator
│   ├── index (Home)
│   ├── explore (Features)
│   └── profile (Profile)
└── Modal/Feature screens (/payments, etc.)

Prerequisites

  • Expo Router basics
  • App running locally

Steps

Step-by-Step Process

1. Create the Screen Component

Create your screen file in the app/ directory: File: apps/mobile/app/your-screen.tsx
import { View } from "react-native";
import { useTheme } from "heroui-native";
import { AppText } from "@/components/app-text";
import { ScreenContainer } from "@/components/screen-container";

export default function YourScreen() {
  const { colors } = useTheme();

  return (
    <ScreenContainer style={{ backgroundColor: colors.background }}>
      <View className="px-5 py-6">
        <AppText className="text-3xl font-bold mb-2">Your Screen Title</AppText>

        <AppText className="text-lg text-muted-foreground mb-6">
          Screen description or subtitle.
        </AppText>

        {/* Your screen content here */}
      </View>
    </ScreenContainer>
  );
}

2. Customize Screen Options (Optional)

Expo Router uses file-based routing, so your screen is registered automatically. If you want custom header options, add them to the layout: File: apps/mobile/app/_layout.tsx or apps/mobile/app/(tabs)/_layout.tsx
<Stack.Screen
  name="your-screen"
  options={{
    title: "Your Screen Title",
    presentation: "card",
    headerShown: true,
  }}
/>

3. Adjust Redirect Rules (if needed)

If the screen should be reachable during onboarding or auth flows, update the redirect logic in apps/mobile/lib/hooks/useAppNavigation.ts.

4. Add Navigation from Other Screens

To navigate to your screen from within tabs (like Explore): From within a tab screen:
import { useNavigation } from "@react-navigation/native";

const navigation = useNavigation();

const handleNavigate = () => {
  const parentNav = navigation.getParent();
  const grandParentNav = parentNav?.getParent();
  grandParentNav?.navigate("your-screen" as never);
};
Using FeatureCard component:
<FeatureCard
  title="Your Feature"
  description="Feature description"
  providers={providers}
  status="Not set up"
  buttonText="Explore Feature"
  onPress={handleNavigate}
/>

From Tabs to AppStack Routes

When navigating from inside tabs to AppStack routes, you need to access the grandparent navigator:
// Inside a tab screen (explore, profile, etc.)
const parentNav = navigation.getParent(); // Gets NativeTabs
const grandParentNav = parentNav?.getParent(); // Gets AppStack
grandParentNav?.navigate("your-screen" as never);

Direct AppStack Navigation

For routes at the same level in AppStack:
// Inside an AppStack screen
navigation.navigate("your-screen" as never);

Back Navigation

Back navigation works automatically with the configured headerBackTitle:
  • iOS: Shows ”< Features” or custom back title
  • Android: Shows standard back arrow
  • Gesture: Swipe from edge works automatically

Screen Types and Presentations

presentation: "card"; // Slides in from right, standard behavior
presentation: "modal"; // Slides up from bottom, modal-style

Fullscreen Presentation

presentation: "fullScreenModal"; // Full screen overlay
Navigation guards live in apps/mobile/lib/hooks/useAppNavigation.ts and handle:
  • Redirecting unauthenticated users to auth
  • Redirecting users with incomplete onboarding
  • Sending fully onboarded users to the main tabs

Common Issues and Solutions

Issue: Navigation Not Working from Tabs

Problem: Calling navigation.navigate() from inside tabs doesn’t work. Solution: Use grandparent navigation:
const parentNav = navigation.getParent();
const grandParentNav = parentNav?.getParent();
grandParentNav?.navigate("your-screen" as never);

Issue: Automatic Redirect Away from Screen

Problem: User gets redirected back to onboarding or tabs. Solution: Update redirect logic in useAppNavigation.ts so the new route is reachable for your target user state.

Issue: Wrong Back Button Text

Problem: Back button shows “(tabs)” or route name. Solution: Set custom back title:
headerBackTitle: "Features"; // Shows "< Features"

Best Practices

1. Consistent Screen Structure

  • Use ScreenContainer for layout consistency
  • Follow the established padding/margin patterns
  • Use theme colors via useTheme()

2. Navigation Naming

  • Use kebab-case for route names: "feature-name"
  • Keep route names descriptive but concise
  • Match file names to route names when possible

3. Header Configuration

  • Always set meaningful title
  • Use appropriate presentation for UX
  • Customize headerBackTitle for better navigation context

4. Navigation Guards

  • Update redirect rules in useAppNavigation.ts when adding new flows
  • Consider authentication requirements
  • Test navigation flows thoroughly

Example: Complete Feature Screen

Here’s a complete example following all best practices: File: apps/mobile/app/ai-chat.tsx
import { View } from "react-native";
import { useTheme } from "heroui-native";
import { AppText } from "@/components/app-text";
import { ScreenContainer } from "@/components/screen-container";

export default function AIChatScreen() {
  const { colors } = useTheme();

  return (
    <ScreenContainer style={{ backgroundColor: colors.background }}>
      <View className="px-5 py-6">
        <AppText className="text-3xl font-bold mb-2">AI Chat</AppText>

        <AppText className="text-lg text-muted-foreground mb-6">
          Configure AI providers for intelligent conversations.
        </AppText>

        {/* Provider selection and setup content */}
      </View>
    </ScreenContainer>
  );
}
AppStack Registration:
<Stack.Screen
  name="ai-chat"
  options={{
    title: "AI Chat",
    presentation: "card",
    headerShown: true,
    headerBackTitle: "Features",
  }}
/>
Navigation from FeatureCard:
<FeatureCard
  title="AI Chat"
  description="Multi-model AI conversations with Claude and OpenAI"
  providers={aiProviders}
  status="Coming soon"
  buttonText="Explore AI Chat"
  onPress={() => {
    const parentNav = navigation.getParent();
    const grandParentNav = parentNav?.getParent();
    grandParentNav?.navigate("ai-chat" as never);
  }}
/>
This pattern provides a consistent, maintainable approach to adding new feature screens to the Launch mobile app.

Troubleshooting

  • Screen not accessible: check route group and auth/onboarding guards
  • Header not updating: update the correct layout file

Next Steps