agentsclimarketplace

Navigation

Skill Shankulkarni/react-native-ai-plugin/plugins/rn-ai/skills/navigation

Write better React Native code from day one - opinionated Claude Code rules, New Architecture ready.

Install
npx -y skills add Shankulkarni/react-native-ai-plugin --skill navigation

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when working with Expo Router -- layouts, auth guards, typed routes, deep linking, tabs, modals, stack navigation, and passing params.

SKILL.md

4.7 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it

Navigation -- Expo Router v4

File-based routing. Every file in app/ is a route. Never string-concatenate paths.

Layout Groups

app/
├── _layout.tsx          ← root layout (providers, fonts, theme)
├── (auth)/              ← guest-only group
│   ├── _layout.tsx      ← redirects logged-in users to (app)
│   ├── login.tsx
│   └── register.tsx
├── (app)/               ← protected group
│   ├── _layout.tsx      ← redirects guests to (auth)/login
│   └── (tabs)/
│       ├── _layout.tsx  ← tab bar
│       ├── index.tsx    ← Home tab
│       └── profile.tsx  ← Profile tab
└── +not-found.tsx

Auth Guard Layouts

app/(auth)/_layout.tsx -- guest only:

import { Redirect, Stack } from 'expo-router'
import { useSession } from '@/hooks/use-session'

export default function AuthLayout() {
  const { session } = useSession()
  if (session) return <Redirect href="/(app)/(tabs)/" />
  return <Stack screenOptions={{ headerShown: false }} />
}

app/(app)/_layout.tsx -- protected:

import { Redirect, Stack } from 'expo-router'
import { useSession } from '@/hooks/use-session'

export default function AppLayout() {
  const { session } = useSession()
  if (!session) return <Redirect href="/(auth)/login" />
  return <Stack screenOptions={{ headerShown: false }} />
}

Tab Navigator

app/(app)/(tabs)/_layout.tsx:

import { Tabs } from 'expo-router'
import { HomeIcon, UserIcon } from 'lucide-react-native'

export default function TabLayout() {
  return (
    <Tabs screenOptions={{ tabBarActiveTintColor: '#6366f1', headerShown: false }}>
      <Tabs.Screen
        name="index"
        options={{ title: 'Home', tabBarIcon: ({ color }) => <HomeIcon color={color} size={24} /> }}
      />
      <Tabs.Screen
        name="profile"
        options={{ title: 'Profile', tabBarIcon: ({ color }) => <UserIcon color={color} size={24} /> }}
      />
    </Tabs>
  )
}

Stack with Header

<Stack>
  <Stack.Screen name="index" options={{ title: 'Home' }} />
  <Stack.Screen name="[id]" options={{ title: 'Detail', headerBackTitle: 'Back' }} />
</Stack>

Typed Routes

Enable in app.json:

{ "expo": { "experiments": { "typedRoutes": true } } }
import { router, Link } from 'expo-router'

// ✅ Typed -- compile-time checked
router.push({ pathname: '/(app)/(tabs)/', params: { tab: 'home' } })
router.push({ pathname: '/(app)/[id]', params: { id: item.id } })

<Link href={{ pathname: '/(app)/[id]', params: { id: item.id } }}>
  Open
</Link>

// ❌ Never string concatenation
router.push(`/items/${item.id}`)

Reading Params

import { useLocalSearchParams, useGlobalSearchParams } from 'expo-router'

// Local -- only params from current route segment
const { id } = useLocalSearchParams<{ id: string }>()

// Global -- params from any segment in the URL
const { tab } = useGlobalSearchParams<{ tab: string }>()

Modal

// app/(app)/modal.tsx
import { router } from 'expo-router'
import { View, Text } from 'react-native'
import { Pressable } from 'react-native'

export default function Modal() {
  return (
    <View className="flex-1 items-center justify-center">
      <Text className="text-lg font-semibold">Modal</Text>
      <Pressable onPress={() => router.back()}>
        <Text>Close</Text>
      </Pressable>
    </View>
  )
}

// In _layout.tsx
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />

Deep Linking

app.json:

{
  "expo": {
    "scheme": "myapp",
    "ios": { "associatedDomains": ["applinks:myapp.com"] },
    "android": { "intentFilters": [{ "action": "VIEW", "data": [{ "scheme": "myapp" }], "category": ["BROWSABLE", "DEFAULT"] }] }
  }
}

Expo Router handles deep links automatically based on file structure -- no extra config needed.

Imperative Navigation

import { router } from 'expo-router'

router.push({ pathname: '/(app)/[id]', params: { id } })
router.replace({ pathname: '/(auth)/login' })
router.back()
router.canGoBack()  // check before calling back()

Not Found

app/+not-found.tsx:

import { Link, Stack } from 'expo-router'
import { View, Text } from 'react-native'

export default function NotFound() {
  return (
    <>
      <Stack.Screen options={{ title: 'Not Found' }} />
      <View className="flex-1 items-center justify-center gap-4">
        <Text className="text-xl font-bold">Page not found</Text>
        <Link href="/(app)/(tabs)/" className="text-primary">Go home</Link>
      </View>
    </>
  )
}

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,970. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.