diff --git a/README.md b/README.md index 8204dac5..af678431 100644 --- a/README.md +++ b/README.md @@ -162,9 +162,11 @@ By adding selected `.mdc` files to `.cursor/rules/`, you can use these rules dir - [Android Native (Jetpack Compose)](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/android-jetpack-compose-cursorrules-prompt-file.mdc) - Android development with Jetpack Compose integration. - [Cursor Rules Pack v2](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/cursor-rules-pack-v2-cursorrules-prompt-file.mdc) - 7 sample production-tested rules (dependency discipline, error handling, state management, webhook security, and more). See the pack README for full-pack details. +- [Expo Router (React Native, Expo)](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/expo-router-cursorrules-prompt-file.mdc) - File-based routing patterns for Expo: layouts, route groups, dynamic and catch-all routes, typed routes, modals, auth gating, and web compatibility. - [Flutter Expert](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/flutter-app-expert-cursorrules-prompt-file.mdc) - Flutter development with expert integration. - [HarmonyOS ArkTS](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/harmony-arkts.mdc) - Components, state, resources, lifecycle, layout, and accessibility. - [NativeScript](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/nativescript-cursorrules-prompt-file.mdc) - Cross-platform mobile app development. +- [NativeWind (React Native, Expo, Tailwind)](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/nativewind-cursorrules-prompt-file.mdc) - Tailwind-in-RN setup, className vs style, what does and doesn't translate from web Tailwind, dark mode, semantic tokens, and platform-specific variants. - [React Native Expo](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/react-native-expo-cursorrules-prompt-file.mdc) - Expo-based mobile app development. - [SwiftUI Guidelines](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/swiftui-guidelines-cursorrules-prompt-file.mdc) - SwiftUI development guidelines. - [TypeScript (Expo, Jest, Detox)](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/typescript-expo-jest-detox-cursorrules-prompt-file.mdc) - TypeScript development with Expo, Jest, and Detox integration. diff --git a/rules/expo-router-cursorrules-prompt-file.mdc b/rules/expo-router-cursorrules-prompt-file.mdc new file mode 100644 index 00000000..418204f9 --- /dev/null +++ b/rules/expo-router-cursorrules-prompt-file.mdc @@ -0,0 +1,94 @@ +--- +description: "Cursor rules for file-based routing with Expo Router in React Native and Expo apps (web + native)." +globs: ["app/**/*.{ts,tsx,js,jsx}", "**/_layout.{ts,tsx}"] +alwaysApply: false +--- + +# Expo Router + +Expo Router is file-based routing for React Native. Every file inside `app/` becomes a route. Layouts wrap nested routes. The same code runs on iOS, Android, and web. + +## Directory structure + +- `app/_layout.tsx` is the root layout. It runs once at app start and must render ``, ``, ``, or ``. +- `app/index.tsx` is the home route (`/`). +- Nested folders create nested URLs: `app/settings/profile.tsx` → `/settings/profile`. +- `app/_layout.tsx` inside a subfolder defines a layout scoped to that subtree. +- Files starting with `_` are not routes (they're layouts or helpers). +- Files starting with `+` are special (e.g., `+not-found.tsx`, `+native-intent.tsx`). + +## Layouts: Stack, Tabs, and groups + +```tsx +// app/_layout.tsx +import { Stack } from "expo-router"; +export default function RootLayout() { + return ( + + + + + + ); +} +``` + +- Use `` for bottom tab navigators, `` for pushed screens, `` for side drawers (requires `expo-router/drawer`). +- Wrap a layout in a route group `(name)` to share a layout without affecting the URL: `app/(tabs)/home.tsx` → `/home`. Groups are how you avoid `/tabs/home` URLs. + +## Dynamic and catch-all routes + +- `app/post/[id].tsx` → `/post/123`. Read with `useLocalSearchParams<{ id: string }>()`. +- `app/blog/[...slug].tsx` is a catch-all, captures `slug` as `string[]`. +- Optional catch-all: `app/blog/[[...slug]].tsx` matches both `/blog` and `/blog/a/b`. + +## Linking and navigation + +- `` is the declarative way. It compiles to `` on web, `` on native. +- `useRouter()` gives imperative `router.push()`, `router.replace()`, `router.back()`, `router.dismiss()`. +- Prefer `` for static destinations — it gets correct accessibility on web. +- Use `router.replace()` for auth flows so the user can't swipe-back to the login screen. + +## Search params and typed routes + +- Read query params with `useLocalSearchParams<{ q?: string }>()`. They are always strings. +- For type safety across the whole router, enable typed routes in `app.json`: `"experiments": { "typedRoutes": true }`. Then `href` is autocompleted and unknown paths are type errors. +- `useGlobalSearchParams` (rarely needed) reads params from the closest matching route, not the current screen. + +## Modals and presentation styles + +- Modal: ``. On iOS this slides up; on web it renders as a normal page (style yourself if you want overlay behavior). +- Transparent modal: `presentation: "transparentModal"` with a Pressable backdrop. +- Always provide a close affordance — modals on Android close on hardware back, but not all web browsers respect that. + +## Authentication patterns + +- Use `` inside layouts to gate subtrees. +- For an auth context, wrap providers in `app/_layout.tsx` so they're available to every route. +- After login, call `router.replace("/(tabs)")` so the auth stack isn't in history. +- Don't read auth state inside the layout's render and conditionally render `` and routes in the same pass — extract a guard component to avoid flicker. + +## Web compatibility + +- Files must compile for native AND web. Don't import native-only APIs at the top of a route file — lazy-load with `Platform.OS === 'web'` checks or use `.web.tsx` / `.native.tsx` extensions. +- For SEO on web, set `` tags inside the route: `import { Stack } from "expo-router"; ` translates to `` on web. +- Deep linking: configure `scheme` in `app.json`. The router uses the same URL structure on web and native — `/post/123` matches both. + +## Common pitfalls + +- "Route not found": every leaf route file must default-export a component. Layouts must default-export too. +- "useLocalSearchParams undefined": params are strings only. Parse to numbers manually. They are `undefined` on the first render of a freshly-pushed route — guard for it. +- "Two routes match": avoid having both `app/post/[id].tsx` and `app/post/index.tsx` if the index should redirect — use `<Redirect>` explicitly. +- "Tabs render briefly during auth check": move the auth check into a layout that renders a loading state, not into the tab screens themselves. +- "Web hot reload loses scroll": expected — use `scrollRestoration` patterns or `react-native-screens` scroll restoration on web. + +## Don'ts + +- Don't use React Navigation `useNavigation()` types directly. Use `useRouter()` from `expo-router` so types match. +- Don't manually call `useEffect(() => router.push(...))` for redirects on first render. Use `<Redirect>` instead — it's synchronous and SSR-safe. +- Don't put business logic in `_layout.tsx`. Layouts should be thin and only set up navigators, providers, and gates. + +## Reference + +- Docs: https://docs.expo.dev/router/introduction/ +- API: https://docs.expo.dev/router/reference/ diff --git a/rules/nativewind-cursorrules-prompt-file.mdc b/rules/nativewind-cursorrules-prompt-file.mdc new file mode 100644 index 00000000..e944c663 --- /dev/null +++ b/rules/nativewind-cursorrules-prompt-file.mdc @@ -0,0 +1,84 @@ +--- +description: "Cursor rules for styling React Native and Expo apps with NativeWind (Tailwind CSS for RN)." +globs: ["**/*.{ts,tsx,js,jsx}"] +alwaysApply: false +--- + +# NativeWind + +NativeWind brings Tailwind CSS to React Native. You write `className="px-4 bg-primary"` on RN components, and NativeWind compiles to RN styles at build time. + +## Setup essentials + +- Babel: add `"nativewind/babel"` to plugins in `babel.config.js`. +- Metro: wrap the config with `withNativeWind(config, { input: "./global.css" })`. +- `global.css` must contain the three Tailwind directives (`@tailwind base; @tailwind components; @tailwind utilities;`) and be imported once from the root layout. +- `tailwind.config.js` must include `content: ["./app/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}"]` — missing paths means classes are tree-shaken away in production. +- Add `nativewind-env.d.ts` referencing `nativewind/types` so `className` is typed on RN components. + +## className vs style + +- Prefer `className` for everything that can be expressed statically. The compiler hoists styles, so it's faster than inline `style={...}`. +- Use `style` only for runtime-computed values (Reanimated shared values, dynamic positioning from gestures, theme tokens not yet expressed in Tailwind). +- Don't mix the two for the same property — pick one source of truth per style attribute. + +## What does and doesn't translate to RN + +NativeWind covers a strict subset of Tailwind. These work: + +- Layout: `flex`, `flex-row`, `items-center`, `justify-between`, `gap-2` +- Spacing: `p-*`, `m-*`, `px-*`, `py-*` +- Sizing: `w-*`, `h-*`, `w-full`, `max-w-md` +- Colors and typography: `bg-*`, `text-*`, `font-bold`, `text-lg` +- Borders and radius: `border`, `border-2`, `rounded-md`, `border-border` +- Opacity, shadow (web only — for native use `shadow-*` with explicit color) +- State variants on `Pressable`: `active:bg-primary/80` + +These do NOT work natively (web only): + +- Pseudo-classes like `:hover`, `:focus-visible`, `:before`, `:after` +- CSS Grid (`grid`, `grid-cols-*`) +- `transition-*` / `animate-*` (use Reanimated instead) +- `cursor-*`, `select-*` + +When you need cross-platform: guard with the `web:` prefix or `Platform.OS === 'web'`. + +## Dark mode + +- Class strategy is the default. Set `darkMode: 'class'` in `tailwind.config.js` (or `'media'` to auto-track system). +- Use `dark:bg-background-900` style classes — no per-theme component forking. +- Read the active scheme with `useColorScheme()` from `nativewind` (not from `react-native`), so the class toggles correctly. + +## Theme tokens + +- Define semantic tokens in `tailwind.config.js` `theme.extend.colors` — e.g., `primary`, `foreground`, `muted`, `card`. Reference these everywhere instead of raw `blue-500`, `gray-700`. +- Spacing scale uses Tailwind defaults (`0`, `0.5`, `1`, `2`, ... `96`). Don't introduce arbitrary values like `p-[13px]` — extend the scale instead. +- Tokens map to CSS variables on web automatically, so the same `bg-primary` works in browser and native. + +## Platform-specific classes + +NativeWind supports platform variants: + +```tsx +<View className="p-4 ios:pt-6 android:pt-4 web:hover:bg-muted" /> +``` + +Useful for status-bar offsets, hover states (web only), and Android-specific tap feedback. + +## Performance + +- Keep classNames static where possible. Conditional classes via `cn()` (clsx + tailwind-merge) are fine, but constructing className strings inside render hot paths with template literals leaks compile-time optimization. +- Lists: don't put complex className on every row in a large `FlatList` if the row recomputes. Extract a memoized row component. +- Animations: NativeWind cannot animate `bg-*` or other utility classes. Use Reanimated's `useAnimatedStyle` and inline `style` for the animated properties. + +## Common pitfalls + +- "Class doesn't apply": check that the file path is in `tailwind.config.js` `content` array, and that `global.css` is imported once from the root layout. +- "Type error on className": missing `nativewind-env.d.ts` or `tsconfig.json` not including it. +- "Colors look different on web vs native": likely raw RGB / hex vs CSS variable mismatch. Define colors with `hsl(var(--primary))` pattern to share definitions cleanly. +- "Reanimated values don't animate": don't try to animate Tailwind classes. Use `style={useAnimatedStyle(...)}` for the animated parts. + +## Reference + +- Docs: https://www.nativewind.dev +- Repo: https://github.com/nativewind/nativewind