Skip to content

Commit 8624255

Browse files
committed
Add NativeWind and Expo Router cursorrules under Mobile Development
1 parent 11ae73c commit 8624255

3 files changed

Lines changed: 180 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,11 @@ By adding selected `.mdc` files to `.cursor/rules/`, you can use these rules dir
162162

163163
- [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.
164164
- [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.
165+
- [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.
165166
- [Flutter Expert](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/flutter-app-expert-cursorrules-prompt-file.mdc) - Flutter development with expert integration.
166167
- [HarmonyOS ArkTS](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/harmony-arkts.mdc) - Components, state, resources, lifecycle, layout, and accessibility.
167168
- [NativeScript](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/nativescript-cursorrules-prompt-file.mdc) - Cross-platform mobile app development.
169+
- [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.
168170
- [React Native Expo](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/react-native-expo-cursorrules-prompt-file.mdc) - Expo-based mobile app development.
169171
- [SwiftUI Guidelines](https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/swiftui-guidelines-cursorrules-prompt-file.mdc) - SwiftUI development guidelines.
170172
- [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.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
---
2+
description: "Cursor rules for file-based routing with Expo Router in React Native and Expo apps (web + native)."
3+
globs: ["app/**/*.{ts,tsx,js,jsx}", "**/_layout.{ts,tsx}"]
4+
alwaysApply: false
5+
---
6+
7+
# Expo Router
8+
9+
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.
10+
11+
## Directory structure
12+
13+
- `app/_layout.tsx` is the root layout. It runs once at app start and must render `<Slot />`, `<Stack />`, `<Tabs />`, or `<Drawer />`.
14+
- `app/index.tsx` is the home route (`/`).
15+
- Nested folders create nested URLs: `app/settings/profile.tsx` → `/settings/profile`.
16+
- `app/_layout.tsx` inside a subfolder defines a layout scoped to that subtree.
17+
- Files starting with `_` are not routes (they're layouts or helpers).
18+
- Files starting with `+` are special (e.g., `+not-found.tsx`, `+native-intent.tsx`).
19+
20+
## Layouts: Stack, Tabs, and groups
21+
22+
```tsx
23+
// app/_layout.tsx
24+
import { Stack } from "expo-router";
25+
export default function RootLayout() {
26+
return (
27+
<Stack>
28+
<Stack.Screen name="index" options={{ title: "Home" }} />
29+
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
30+
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
31+
</Stack>
32+
);
33+
}
34+
```
35+
36+
- Use `<Tabs>` for bottom tab navigators, `<Stack>` for pushed screens, `<Drawer>` for side drawers (requires `expo-router/drawer`).
37+
- 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.
38+
39+
## Dynamic and catch-all routes
40+
41+
- `app/post/[id].tsx` → `/post/123`. Read with `useLocalSearchParams<{ id: string }>()`.
42+
- `app/blog/[...slug].tsx` is a catch-all, captures `slug` as `string[]`.
43+
- Optional catch-all: `app/blog/[[...slug]].tsx` matches both `/blog` and `/blog/a/b`.
44+
45+
## Linking and navigation
46+
47+
- `<Link href="/settings">` is the declarative way. It compiles to `<a>` on web, `<Pressable>` on native.
48+
- `useRouter()` gives imperative `router.push()`, `router.replace()`, `router.back()`, `router.dismiss()`.
49+
- Prefer `<Link>` for static destinations — it gets correct accessibility on web.
50+
- Use `router.replace()` for auth flows so the user can't swipe-back to the login screen.
51+
52+
## Search params and typed routes
53+
54+
- Read query params with `useLocalSearchParams<{ q?: string }>()`. They are always strings.
55+
- 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.
56+
- `useGlobalSearchParams` (rarely needed) reads params from the closest matching route, not the current screen.
57+
58+
## Modals and presentation styles
59+
60+
- Modal: `<Stack.Screen name="modal" options={{ presentation: "modal" }} />`. On iOS this slides up; on web it renders as a normal page (style yourself if you want overlay behavior).
61+
- Transparent modal: `presentation: "transparentModal"` with a Pressable backdrop.
62+
- Always provide a close affordance — modals on Android close on hardware back, but not all web browsers respect that.
63+
64+
## Authentication patterns
65+
66+
- Use `<Redirect href="/login" />` inside layouts to gate subtrees.
67+
- For an auth context, wrap providers in `app/_layout.tsx` so they're available to every route.
68+
- After login, call `router.replace("/(tabs)")` so the auth stack isn't in history.
69+
- Don't read auth state inside the layout's render and conditionally render `<Redirect>` and routes in the same pass — extract a guard component to avoid flicker.
70+
71+
## Web compatibility
72+
73+
- 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.
74+
- For SEO on web, set `<head>` tags inside the route: `import { Stack } from "expo-router"; <Stack.Screen options={{ title: "Page title" }} />` translates to `<title>` on web.
75+
- Deep linking: configure `scheme` in `app.json`. The router uses the same URL structure on web and native — `/post/123` matches both.
76+
77+
## Common pitfalls
78+
79+
- "Route not found": every leaf route file must default-export a component. Layouts must default-export too.
80+
- "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.
81+
- "Two routes match": avoid having both `app/post/[id].tsx` and `app/post/index.tsx` if the index should redirect — use `<Redirect>` explicitly.
82+
- "Tabs render briefly during auth check": move the auth check into a layout that renders a loading state, not into the tab screens themselves.
83+
- "Web hot reload loses scroll": expected — use `scrollRestoration` patterns or `react-native-screens` scroll restoration on web.
84+
85+
## Don'ts
86+
87+
- Don't use React Navigation `useNavigation()` types directly. Use `useRouter()` from `expo-router` so types match.
88+
- Don't manually call `useEffect(() => router.push(...))` for redirects on first render. Use `<Redirect>` instead — it's synchronous and SSR-safe.
89+
- Don't put business logic in `_layout.tsx`. Layouts should be thin and only set up navigators, providers, and gates.
90+
91+
## Reference
92+
93+
- Docs: https://docs.expo.dev/router/introduction/
94+
- API: https://docs.expo.dev/router/reference/
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
description: "Cursor rules for styling React Native and Expo apps with NativeWind (Tailwind CSS for RN)."
3+
globs: ["**/*.{ts,tsx,js,jsx}"]
4+
alwaysApply: false
5+
---
6+
7+
# NativeWind
8+
9+
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.
10+
11+
## Setup essentials
12+
13+
- Babel: add `"nativewind/babel"` to plugins in `babel.config.js`.
14+
- Metro: wrap the config with `withNativeWind(config, { input: "./global.css" })`.
15+
- `global.css` must contain the three Tailwind directives (`@tailwind base; @tailwind components; @tailwind utilities;`) and be imported once from the root layout.
16+
- `tailwind.config.js` must include `content: ["./app/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}"]` — missing paths means classes are tree-shaken away in production.
17+
- Add `nativewind-env.d.ts` referencing `nativewind/types` so `className` is typed on RN components.
18+
19+
## className vs style
20+
21+
- Prefer `className` for everything that can be expressed statically. The compiler hoists styles, so it's faster than inline `style={...}`.
22+
- Use `style` only for runtime-computed values (Reanimated shared values, dynamic positioning from gestures, theme tokens not yet expressed in Tailwind).
23+
- Don't mix the two for the same property — pick one source of truth per style attribute.
24+
25+
## What does and doesn't translate to RN
26+
27+
NativeWind covers a strict subset of Tailwind. These work:
28+
29+
- Layout: `flex`, `flex-row`, `items-center`, `justify-between`, `gap-2`
30+
- Spacing: `p-*`, `m-*`, `px-*`, `py-*`
31+
- Sizing: `w-*`, `h-*`, `w-full`, `max-w-md`
32+
- Colors and typography: `bg-*`, `text-*`, `font-bold`, `text-lg`
33+
- Borders and radius: `border`, `border-2`, `rounded-md`, `border-border`
34+
- Opacity, shadow (web only — for native use `shadow-*` with explicit color)
35+
- State variants on `Pressable`: `active:bg-primary/80`
36+
37+
These do NOT work natively (web only):
38+
39+
- Pseudo-classes like `:hover`, `:focus-visible`, `:before`, `:after`
40+
- CSS Grid (`grid`, `grid-cols-*`)
41+
- `transition-*` / `animate-*` (use Reanimated instead)
42+
- `cursor-*`, `select-*`
43+
44+
When you need cross-platform: guard with the `web:` prefix or `Platform.OS === 'web'`.
45+
46+
## Dark mode
47+
48+
- Class strategy is the default. Set `darkMode: 'class'` in `tailwind.config.js` (or `'media'` to auto-track system).
49+
- Use `dark:bg-background-900` style classes — no per-theme component forking.
50+
- Read the active scheme with `useColorScheme()` from `nativewind` (not from `react-native`), so the class toggles correctly.
51+
52+
## Theme tokens
53+
54+
- 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`.
55+
- Spacing scale uses Tailwind defaults (`0`, `0.5`, `1`, `2`, ... `96`). Don't introduce arbitrary values like `p-[13px]` — extend the scale instead.
56+
- Tokens map to CSS variables on web automatically, so the same `bg-primary` works in browser and native.
57+
58+
## Platform-specific classes
59+
60+
NativeWind supports platform variants:
61+
62+
```tsx
63+
<View className="p-4 ios:pt-6 android:pt-4 web:hover:bg-muted" />
64+
```
65+
66+
Useful for status-bar offsets, hover states (web only), and Android-specific tap feedback.
67+
68+
## Performance
69+
70+
- 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.
71+
- Lists: don't put complex className on every row in a large `FlatList` if the row recomputes. Extract a memoized row component.
72+
- Animations: NativeWind cannot animate `bg-*` or other utility classes. Use Reanimated's `useAnimatedStyle` and inline `style` for the animated properties.
73+
74+
## Common pitfalls
75+
76+
- "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.
77+
- "Type error on className": missing `nativewind-env.d.ts` or `tsconfig.json` not including it.
78+
- "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.
79+
- "Reanimated values don't animate": don't try to animate Tailwind classes. Use `style={useAnimatedStyle(...)}` for the animated parts.
80+
81+
## Reference
82+
83+
- Docs: https://www.nativewind.dev
84+
- Repo: https://github.com/nativewind/nativewind

0 commit comments

Comments
 (0)