Skip to content
Back to skills

Mobile

ASecurity

React Native, Expo, mobile apps. Auto-use for mobile work.

  • 57 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 31, 2026
developmenttypescriptbashreacttestingapibackend

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned May 31, 2026

npx -y skills add diegosouzapw/awesome-omni-skill --skill mobile --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mobile?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Mobile
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/diegosouzapw-mobile/badge)](https://www.skillsdirectory.com/skills/diegosouzapw-mobile)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: mobile
description: "React Native, Expo, mobile apps. Auto-use for mobile work."
version: 2.0.0
allowed-tools:
  - Read
  - Edit
  - Write
  - Bash
  - Grep
  - Glob
---

# Mobile

**Auto-use when:** Expo, React Native, mobile, app, NativeWind, iOS, Android

**Works with:** `backend` for API, `quality` for testing

---

## Quick Setup

### File Structure
```
app/
├── _layout.tsx        # Root (providers, fonts)
├── index.tsx          # Home
├── (auth)/            # Auth group
│   ├── _layout.tsx
│   └── login.tsx
├── (tabs)/            # Tab group (authenticated)
│   ├── _layout.tsx
│   ├── index.tsx
│   └── profile.tsx
└── [id].tsx           # Dynamic route
```

### Root Layout
```typescript
// app/_layout.tsx
import { Stack } from 'expo-router'
import { QueryClientProvider } from '@tanstack/react-query'
import '../global.css'

export default function RootLayout() {
  return (
    <QueryClientProvider client={queryClient}>
      <AuthProvider>
        <Stack screenOptions={{ headerShown: false }} />
      </AuthProvider>
    </QueryClientProvider>
  )
}
```

### Protected Tabs
```typescript
// app/(tabs)/_layout.tsx
import { Tabs, Redirect } from 'expo-router'
import { useAuth } from '@/providers/auth'

export default function TabLayout() {
  const { session, isLoading } = useAuth()

  if (isLoading) return null
  if (!session) return <Redirect href="/login" />

  return (
    <Tabs>
      <Tabs.Screen name="index" options={{ title: 'Home' }} />
      <Tabs.Screen name="profile" options={{ title: 'Profile' }} />
    </Tabs>
  )
}
```

---

## NativeWind

```typescript
// Usage (Tailwind syntax)
<View className="flex-1 bg-white p-4">
  <Text className="text-lg font-bold text-gray-900">Title</Text>
  <Pressable className="bg-blue-500 p-4 rounded-lg active:opacity-70">
    <Text className="text-white text-center">Button</Text>
  </Pressable>
</View>

// Platform-specific
<View className="ios:pt-12 android:pt-8" />

// Dark mode
<Text className="text-gray-900 dark:text-white" />
```

---

## Supabase Mobile

### Client
```typescript
// lib/supabase.ts
import AsyncStorage from '@react-native-async-storage/async-storage'
import { createClient } from '@supabase/supabase-js'

export const supabase = createClient(url, key, {
  auth: {
    storage: AsyncStorage,
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: false,  // Important for mobile
  },
})
```

### Realtime with Cleanup (CRITICAL)
```typescript
useEffect(() => {
  let channel: RealtimeChannel

  const setup = async () => {
    const { data } = await supabase.from('items').select('*')
    setData(data || [])

    channel = supabase
      .channel(`room:${roomId}`)
      .on('postgres_changes', { event: '*', schema: 'public', table: 'items' },
        (payload) => setData(prev => [...prev, payload.new])
      )
      .subscribe()
  }

  setup()

  // CRITICAL: Cleanup
  return () => {
    if (channel) supabase.removeChannel(channel)
  }
}, [roomId])
```

---

## Common Patterns

### Navigation
```typescript
import { router, useLocalSearchParams, Link } from 'expo-router'

router.push('/profile')
router.push({ pathname: '/user/[id]', params: { id: '123' } })
router.replace('/login')
router.back()

const { id } = useLocalSearchParams<{ id: string }>()
```

### Platform-Specific
```typescript
import { Platform } from 'react-native'

const padding = Platform.OS === 'ios' ? 50 : 30

const styles = Platform.select({
  ios: { shadowColor: '#000', shadowOpacity: 0.1 },
  android: { elevation: 4 },
})
```

### Safe Area
```typescript
import { useSafeAreaInsets } from 'react-native-safe-area-context'

const insets = useSafeAreaInsets()
<View style={{ paddingTop: insets.top }} />

// Or with NativeWind
<View className="pt-safe pb-safe" />
```

---

## EAS Commands

```bash
# Development build
eas build --profile development --platform ios

# Preview (internal testing)
eas build --profile preview --platform all

# Production
eas build --profile production --platform all

# Submit to stores
eas submit --platform ios
eas submit --platform android

# OTA update
eas update --branch production --message "Bug fixes"
```

---

## Package Installation

```bash
# ALWAYS use npx expo install (not npm install)
npx expo install package-name

# Common packages
npx expo install react-native-reanimated
npx expo install @react-native-async-storage/async-storage
npx expo install expo-image-picker
npx expo install expo-secure-store
```

---

## Checklist

```
[] Uses REAL data (not mock)
[] Loading states
[] Error states
[] Empty states
[] Cleanup subscriptions on unmount
[] Use expo-secure-store for secrets
[] HTTPS for all API calls
[] Platform-specific handling where needed
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.