NativeWind + Zustand + React Query
Hereβs a production-ready setup for combining Expo + Bun with:
- π¨ NativeWind (styling)
- π§ Zustand (state)
- β‘ TanStack Query (API + caching)
This is structured exactly how senior teams ship scalable apps.
π 1. Install Dependencies (Bun)
Section titled βπ 1. Install Dependencies (Bun)βbun add nativewind react-native-reanimated react-native-safe-area-contextbun add zustand @tanstack/react-query axiosbun add -d tailwindcssThen:
bunx tailwindcss initπ¨ 2. NativeWind Setup
Section titled βπ¨ 2. NativeWind Setupββ
tailwind.config.js
Section titled ββ
tailwind.config.jsβ/** @type {import('tailwindcss').Config} */module.exports = { content: [ "./app/**/*.{js,ts,tsx}", "./src/**/*.{js,ts,tsx}", ], theme: { extend: {}, }, plugins: [],};β
babel.config.js
Section titled ββ
babel.config.jsβmodule.exports = function (api) { api.cache(true); return { presets: ['babel-preset-expo'], plugins: ['nativewind/babel'], };};β Example Usage
Section titled ββ Example Usageβimport { View, Text } from 'react-native';
export default function Home() { return ( <View className="flex-1 items-center justify-center bg-white"> <Text className="text-xl font-bold text-blue-500"> Hello NativeWind π </Text> </View> );}π§ 3. Zustand Setup (Global State)
Section titled βπ§ 3. Zustand Setup (Global State)βπ src/store/authStore.ts
Section titled βπ src/store/authStore.tsβimport { create } from 'zustand';
type AuthState = { user: null | { id: string; name: string }; setUser: (user: AuthState['user']) => void; logout: () => void;};
export const useAuthStore = create<AuthState>((set) => ({ user: null, setUser: (user) => set({ user }), logout: () => set({ user: null }),}));β Usage
Section titled ββ Usageβimport { useAuthStore } from '@/store/authStore';
const user = useAuthStore((state) => state.user);β‘ 4. React Query Setup (API Layer)
Section titled ββ‘ 4. React Query Setup (API Layer)βπ src/lib/react-query.ts
Section titled βπ src/lib/react-query.tsβimport { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 2, staleTime: 1000 * 60 * 5, // 5 mins }, },});π src/services/api/client.ts
Section titled βπ src/services/api/client.tsβimport axios from 'axios';
export const apiClient = axios.create({ baseURL: 'https://api.example.com', timeout: 10000,});
apiClient.interceptors.request.use((config) => { // attach token here return config;});π src/features/user/api.ts
Section titled βπ src/features/user/api.tsβimport { apiClient } from '@/services/api/client';
export const getUser = async () => { const { data } = await apiClient.get('/user'); return data;};π src/features/user/hooks.ts
Section titled βπ src/features/user/hooks.tsβimport { useQuery } from '@tanstack/react-query';import { getUser } from './api';
export const useUser = () => { return useQuery({ queryKey: ['user'], queryFn: getUser, });};π 5. Provider Setup (VERY IMPORTANT)
Section titled βπ 5. Provider Setup (VERY IMPORTANT)βπ app/_layout.tsx
Section titled βπ app/_layout.tsxβimport { QueryClientProvider } from '@tanstack/react-query';import { queryClient } from '@/lib/react-query';import { Stack } from 'expo-router';
export default function RootLayout() { return ( <QueryClientProvider client={queryClient}> <Stack /> </QueryClientProvider> );}π§ͺ 6. Example Screen (Everything Combined)
Section titled βπ§ͺ 6. Example Screen (Everything Combined)βimport { View, Text, Button } from 'react-native';import { useUser } from '@/features/user/hooks';import { useAuthStore } from '@/store/authStore';
export default function Profile() { const { data, isLoading } = useUser(); const setUser = useAuthStore((s) => s.setUser);
if (isLoading) return <Text>Loading...</Text>;
return ( <View className="flex-1 p-4 bg-white"> <Text className="text-xl font-bold"> {data?.name} </Text>
<Button title="Save to Store" onPress={() => setUser(data)} /> </View> );}βοΈ 7. Recommended Add-ons (Optional but Pro)
Section titled ββοΈ 7. Recommended Add-ons (Optional but Pro)β| Purpose | Library |
|---|---|
| Forms | react-hook-form + zod |
| Storage | expo-secure-store / mmkv |
| Icons | @expo/vector-icons |
| Animations | react-native-reanimated |
| Environment | expo-constants |
π Dev Workflow with Bun
Section titled βπ Dev Workflow with Bunβbun installbunx expo startπ₯ Senior Tips (Real-World)
Section titled βπ₯ Senior Tips (Real-World)β- Keep API logic inside features, not global
- Use React Query for server state ONLY
- Use Zustand for UI/global state
- Donβt overuse global store β prefer feature isolation
- Add error boundaries + retry UI early
If you want next step, I can build you:
- π₯ A complete starter repo (copy-paste ready)
- π§ Auth system (JWT + refresh + secure storage)
- π¦ Offline-first setup (React Query persistence + caching)
- π CI/CD with Expo EAS + environments