State Management
Learn about VibeFast's simple and powerful approach to managing server and client state.
VibeFast follows a modern and streamlined approach to state management, designed to be simple, powerful, and easy for both humans and AI to reason about.
The Philosophy: Server-First
The core principle is to keep as much state as possible on the server. Your Convex backend is the single source of truth for your application's data. This approach dramatically simplifies the client, reduces bugs, and ensures data consistency across all users and devices in real-time.
For this reason, we do not use complex client-side state managers like Redux or MobX.
Server State: Convex Hooks
For any data that needs to be persisted in your database, you should always use Convex's built-in hooks. They are your primary tool for state management.
Reading Data with useQuery
To read data from your backend, use the useQuery
hook. It automatically provides real-time updates, so your UI will always be in sync with your database.
import { useQuery } from 'convex/react';
import { api } from 'convex/_generated/api';
function UserProfile({ userId }) {
// This hook automatically fetches and subscribes to data changes.
const user = useQuery(api.users.getProfile, { id: userId });
if (!user) {
return <p>Loading...</p>;
}
return <div>{user.name}</div>;
}
Writing Data with useMutation
To write or modify data, use the useMutation
hook.
import { useMutation } from 'convex/react';
import { api } from 'convex/_generated/api';
function EditProfileForm({ userId }) {
const updateName = useMutation(api.users.updateName);
const handleSave = () => {
updateName({ id: userId, newName: 'Jane Doe' });
};
return <button onClick={handleSave}>Save</button>;
}```
### Client State: Zustand for UI State
There are rare cases where you need to manage global state that is purely related to the client-side UI and doesn't need to be persisted on the server. For these scenarios, VibeFast uses **Zustand**.
Zustand is a minimal, fast, and unopinionated state management library. In VibeFast, it is used for one specific purpose: tracking the global authentication status for the UI.
You can see this implementation in `src/core/state/auth-client-slice.ts`.
<div className="fd-callout fd-callout-info">
**When to Use Zustand?**
Only reach for Zustand if you have UI state that needs to be shared across many disconnected components and does **not** belong on the server. A good example might be the open/closed state of a global music player. For everything else, use Convex.
</div>
### Summary: When to Use What
| Type of State | Tool to Use | Why? |
| --------------------------------------------- | ------------------ | ------------------------------------------------------------------- |
| User data, chat messages, settings, etc. | **Convex `useQuery`** | It's your real-time, persistent source of truth. |
| Creating, updating, or deleting data | **Convex `useMutation`** | It handles server-side logic and keeps data consistent. |
| Global, non-persistent UI state (e.g., auth status) | **Zustand** | For simple, ephemeral state that doesn't belong on the server. |
| State for a single component (e.g., form input) | **`useState`** | The simplest solution for local, component-level state. |
By following this server-first approach, you can build complex, real-time applications with surprisingly little client-side state management code.