--- name: solito description: This skill should be used when working on Solito projects (Expo + Next.js with shared navigation). It provides specialized knowledge for cross-platform navigation, monorepo structure, shared component patterns, platform-specific code handling, and common pitfalls. Use this skill when building universal apps, implementing navigation, creating shared UI components, or debugging cross-platform issues. --- # Solito Cross-Platform Development Skill A specialized skill for building universal applications using Solito, combining Expo (React Native) and Next.js with shared navigation and component libraries. ## When to Use This Skill Invoke this skill when: - Building universal apps (web + mobile) with shared code - Implementing cross-platform navigation - Creating shared UI components that work on web and native - Setting up monorepo structure for Solito projects - Handling platform-specific code (web-only or native-only features) - Implementing authentication across platforms - Debugging navigation or routing issues - Optimizing bundle sizes for web and native ## What is Solito? Solito enables developers to share navigation code between React Native (Expo) and Next.js. Instead of maintaining separate routing logic for web and mobile, Solito provides a unified navigation layer that respects platform-specific patterns while sharing as much code as possible. **Key Benefits:** - Single source of truth for navigation - File-system routing for native apps (via Expo Router) - Shared UI components with platform-specific optimizations - Unified API for links and navigation - Type-safe routing with TypeScript ## Before You Start: Essential Solito Tips ### Project Structure First **Solito projects use monorepo architecture.** Always structure your project correctly from the start: ``` my-app/ apps/ expo/ # React Native mobile app next/ # Next.js web app packages/ app/ # Shared UI components & navigation api/ # Shared API client (optional) package.json # Workspace configuration ``` **See `references/monorepo-setup.md` for detailed workspace configuration.** ### Use Solito Starter Template The official Solito starter saves hours of configuration: ```bash npx create-solito-app@latest my-app ``` Choose your preferred package manager (pnpm recommended for monorepos). ### Navigation is Different **Don't use Next.js Link or React Navigation Link directly.** Use Solito's unified components: ```tsx import { Link } from 'solito/link' // ✅ CORRECT: Works on both web and native View Profile // ❌ WRONG: Next.js-only import Link from 'next/link' // ❌ WRONG: React Navigation-only import { Link } from '@react-navigation/native' ``` **See `references/navigation-patterns.md` for detailed routing patterns.** ## Core Development Principles ### 1. Shared by Default, Platform-Specific When Needed **80% of your code should live in `packages/app`.** Only create platform-specific code when absolutely necessary. ```tsx // packages/app/features/home/screen.tsx // ✅ GOOD: Shared screen component export function HomeScreen() { return ( Welcome! Go to Profile ) } ``` Platform-specific code should be minimal: ```tsx // packages/app/components/map.tsx import { Platform } from 'react-native' export function Map() { // ✅ GOOD: Platform detection for necessary differences if (Platform.OS === 'web') { return } return } ``` ### 2. Think in Features, Not Platforms **Organize by feature domain, not by platform:** ``` packages/app/ features/ home/ screen.tsx # Home screen components/ # Home-specific components profile/ screen.tsx # Profile screen components/ auth/ login-screen.tsx register-screen.tsx components/ # Shared UI components provider/ # Navigation provider navigation/ # Navigation configuration ``` **Not organized by platform:** ``` ❌ BAD: packages/app/ web/ screens/ native/ screens/ ``` ### 3. Navigation is Central **Design your navigation structure early.** Solito uses file-system routing on native and Next.js App Router on web. **Navigation mapping:** ```tsx // apps/next/app/profile/page.tsx (web route) import { ProfileScreen } from 'app/features/profile/screen' export default ProfileScreen // apps/expo/app/profile.tsx (native route) import { ProfileScreen } from 'app/features/profile/screen' export default ProfileScreen ``` Both platforms render the same `ProfileScreen` component from `packages/app`. **See `references/navigation-patterns.md` for routing patterns, params, and navigation hooks.** ## Development Workflow ### Local Development **Run both platforms simultaneously:** Terminal 1 - Web: ```bash cd apps/next pnpm dev ``` Terminal 2 - Mobile: ```bash cd apps/expo pnpm start ``` Press `i` for iOS simulator, `a` for Android emulator, or scan QR code for physical device. ### Making Changes 1. **Create shared component** in `packages/app/components/` 2. **Create screen** in `packages/app/features/[feature]/screen.tsx` 3. **Add routes** in both `apps/next/app/` and `apps/expo/app/` 4. **Test on both platforms** to verify cross-platform behavior ### Common Commands ```bash # Install dependencies (from root) pnpm install # Run web dev server cd apps/next && pnpm dev # Run Expo dev server cd apps/expo && pnpm start # Type checking pnpm typecheck # Lint all packages pnpm lint # Build for production pnpm build ``` ## Styling with NativeWind NativeWind brings Tailwind CSS to React Native, enabling shared styling across platforms. ```tsx import { View, Text } from 'react-native' // ✅ GOOD: Tailwind classes work on both platforms export function Card() { return ( Title Description ) } ``` **Platform-specific styles when needed:** ```tsx {/* Different padding on web vs native */} ``` ### Design Tokens **Use shared design tokens in `packages/app/design/tokens.ts`:** ```tsx export const colors = { primary: '#3B82F6', secondary: '#10B981', background: '#FFFFFF', text: '#1F2937', } export const spacing = { xs: 4, sm: 8, md: 16, lg: 24, xl: 32, } ``` ## Authentication Patterns **Share auth logic across platforms:** ```tsx // packages/app/lib/auth.ts import { createContext, useContext } from 'react' interface AuthContextType { user: User | null signIn: (email: string, password: string) => Promise signOut: () => Promise } export const AuthContext = createContext(null!) export function useAuth() { return useContext(AuthContext) } ``` **Use in components:** ```tsx import { useAuth } from 'app/lib/auth' export function ProfileScreen() { const { user, signOut } = useAuth() return ( Welcome, {user?.name} ) } ``` **Popular auth solutions:** - **Clerk** - Excellent Solito integration, works on both platforms - **Supabase** - Self-hosted option with good RN support - **Custom JWT** - Full control, requires more setup ## API Integration **Share API client in `packages/api/`:** ```tsx // packages/api/client.ts const API_URL = process.env.NEXT_PUBLIC_API_URL export async function fetchUser(id: string) { const response = await fetch(`${API_URL}/users/${id}`) return response.json() } ``` **Use with tRPC for type-safety:** ```tsx // packages/api/trpc.ts import { initTRPC } from '@trpc/server' const t = initTRPC.create() export const appRouter = t.router({ getUser: t.procedure .input(z.string()) .query(async ({ input }) => { return db.user.findUnique({ where: { id: input } }) }), }) export type AppRouter = typeof appRouter ``` **tRPC works seamlessly across web and native with full type-safety.** ## State Management **Recommended: Zustand** (lightweight, works great with Solito) ```tsx // packages/app/store/user.ts import { create } from 'zustand' interface UserState { user: User | null setUser: (user: User | null) => void } export const useUserStore = create((set) => ({ user: null, setUser: (user) => set({ user }), })) ``` **Use in components:** ```tsx import { useUserStore } from 'app/store/user' export function ProfileScreen() { const user = useUserStore((state) => state.user) return Welcome, {user?.name} } ``` **Other options:** - **Jotai** - Atomic state management - **Redux Toolkit** - For complex state needs - **React Context** - For simple global state ## Platform-Specific Code **Use platform detection sparingly:** ```tsx import { Platform } from 'react-native' export function VideoPlayer() { if (Platform.OS === 'web') { return