📅 Day 17
💡 tip

State Management in 2025: Zustand, TanStack Query & Jotai

A practical guide to modern state management in React Native - learn when to use Zustand, TanStack Query, and Jotai for different scenarios

State Management

Welcome to Day 17 of the React Native Advent Calendar!

Today we’re exploring modern state management solutions for React Native in 2026. Forget complex Redux setups - these three libraries make state management simple, performant, and enjoyable!


Why Not Redux?

Redux is still great for large enterprise apps, but modern alternatives offer:

  • Less boilerplate - No actions, reducers, or dispatch
  • Better TypeScript - Automatic type inference
  • Simpler learning curve - Less concepts to master
  • Specialized tools - Right tool for the right job

Let’s explore the top three options.


1. Zustand: Simple Client State

Zustand is a tiny state management library that feels like using React hooks. Perfect for client-side app state. And it’s been one of the fastest growing state management libraries in recent years.

Quick Example

stores/useUserStore.ts
import { create } from 'zustand';

interface UserStore {
	name: string;
	age: number;
	setName: (name: string) => void;
	incrementAge: () => void;
}

export const useUserStore = create<UserStore>((set) => ({
	name: 'John',
	age: 25,
	setName: (name) => set({ name }),
	incrementAge: () => set((state) => ({ age: state.age + 1 }))
}));

Using the Store

components/Profile.tsx
import { useUserStore } from './stores/useUserStore';

export default function Profile() {
	const name = useUserStore((state) => state.name);
	const incrementAge = useUserStore((state) => state.incrementAge);

	return (
		<View>
			<Text>{name}</Text>
			<Button onPress={incrementAge} title="Birthday!" />
		</View>
	);
}

Why it’s great:

  • Zero boilerplate
  • No context providers needed
  • Automatic re-renders (only when selected state changes)
  • Tiny bundle size (1kb)
  • DevTools support

Best for:

  • UI state (theme, modals, navigation)
  • Form state
  • Shopping carts
  • Any client-side application state

2. TanStack Query: Server State Made Easy

TanStack Query (formerly React Query) handles fetching, caching, and synchronizing server data.

Quick Example

hooks/useUsers.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

// Fetch users
export function useUsers() {
	return useQuery({
		queryKey: ['users'],
		queryFn: async () => {
			const response = await fetch('https://api.example.com/users');
			return response.json();
		}
	});
}

// Create user
export function useCreateUser() {
	const queryClient = useQueryClient();

	return useMutation({
		mutationFn: async (newUser: { name: string; email: string }) => {
			const response = await fetch('https://api.example.com/users', {
				method: 'POST',
				body: JSON.stringify(newUser)
			});
			return response.json();
		},
		onSuccess: () => {
			// Invalidate and refetch
			queryClient.invalidateQueries({ queryKey: ['users'] });
		}
	});
}

Using the Hooks

screens/UsersList.tsx
import { useUsers, useCreateUser } from './hooks/useUsers';

export default function UsersList() {
	const { data: users, isLoading, error } = useUsers();
	const createUser = useCreateUser();

	if (isLoading) return <Text>Loading...</Text>;
	if (error) return <Text>Error: {error.message}</Text>;

	return (
		<View>
			<FlatList
				data={users}
				renderItem={({ item }) => <Text>{item.name}</Text>}
				keyExtractor={(item) => item.id}
			/>
			<Button
				title="Add User"
				onPress={() => createUser.mutate({ name: 'Jane', email: 'jane@example.com' })}
			/>
		</View>
	);
}

Why it’s great:

  • Automatic background refetching
  • Built-in caching
  • Optimistic updates
  • Request deduplication
  • Offline support
  • DevTools

Best for:

  • API data fetching
  • Real-time data synchronization
  • Infinite scroll / pagination
  • Any server state

3. Jotai: Atomic State Management

Jotai takes an atomic approach - think of it as tiny, composable pieces of state.

Quick Example

atoms/userAtoms.ts
import { atom } from 'jotai';

// Primitive atoms
export const nameAtom = atom('John');
export const ageAtom = atom(25);

// Derived atom (computed value)
export const greetingAtom = atom((get) => {
	const name = get(nameAtom);
	const age = get(ageAtom);
	return `Hello, ${name}! You are ${age} years old.`;
});

// Write-only atom (action)
export const incrementAgeAtom = atom(null, (get, set) => {
	const currentAge = get(ageAtom);
	set(ageAtom, currentAge + 1);
});

Using Atoms

components/Profile.tsx
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import { nameAtom, greetingAtom, incrementAgeAtom } from './atoms/userAtoms';

export default function Profile() {
	const [name, setName] = useAtom(nameAtom);
	const greeting = useAtomValue(greetingAtom);
	const incrementAge = useSetAtom(incrementAgeAtom);

	return (
		<View>
			<TextInput value={name} onChangeText={setName} />
			<Text>{greeting}</Text>
			<Button onPress={() => incrementAge()} title="Birthday!" />
		</View>
	);
}

Why it’s great:

  • Minimal boilerplate
  • Excellent TypeScript support
  • Automatic dependency tracking
  • Computed values (derived atoms)
  • Built-in React Suspense support

Best for:

  • Complex state dependencies
  • Computed/derived values
  • State that needs to be shared across many components
  • Forms with dependent fields

When to Use Each Solution

Here’s a quick decision guide:

Use Zustand when:

  • ✅ You need simple, global client state
  • ✅ You want minimal setup
  • ✅ You’re managing UI state (themes, modals, etc.)
  • ✅ You want something familiar to Redux but simpler

Use TanStack Query when:

  • ✅ You’re fetching data from APIs
  • ✅ You need automatic caching and background refetching
  • ✅ You want optimistic UI updates
  • ✅ You’re building real-time features

Use Jotai when:

  • ✅ You have complex state relationships
  • ✅ You need many small, composable pieces of state
  • ✅ You want automatic derived values
  • ✅ You prefer a more functional approach

Can you use them together?

Absolutely! In fact, it’s recommended:

  • TanStack Query for all server data
  • Zustand or Jotai for client state
  • Choose Zustand for simplicity, Jotai for complexity

Wrapping Up

Modern state management doesn’t need to be complicated. These three libraries cover all your needs.

Ready to dive deeper?

Which state management library do you prefer? Share your thoughts on Twitter!

Tomorrow we’ll explore Day 18’s topic - see you then! 🎄