Skip to main content

ADR-010: Use TanStack Query and React Context for Frontend State Management

Status

Accepted

Date

YYYY-MM-DD

Context

Chilarai requires frontend state management for two fundamentally different categories of data:

Server State

Data that originates from backend APIs.

Examples:

  • Students
  • Admissions
  • Staff
  • Attendance
  • Fees
  • Academic Structure
  • School Configuration

Server state requires:

  • API communication
  • Caching
  • Refetching
  • Retry handling
  • Background updates
  • Pagination
  • Query invalidation

Application State

Data that exists only within the browser.

Examples:

  • Active School Context
  • Theme Preferences
  • Sidebar State
  • UI Preferences
  • Current Navigation Context

Application state does not originate from backend APIs.

The architecture should:

  • Minimize complexity
  • Avoid duplicate state
  • Prevent multiple sources of truth
  • Remain easy for new developers to understand
  • Scale as the application grows

Decision

Chilarai will use:

  • TanStack Query for Server State
  • React Context for Application State

The platform will not use Redux or Zustand during the initial implementation.

Business data retrieved from APIs must be managed exclusively through TanStack Query.

Application state must be managed through React Context.


Rationale

Clear Separation of Concerns

Server State and Application State have different requirements.

TanStack Query is optimized for data that originates from backend services.

React Context is sufficient for lightweight browser-only state.

This separation creates a clear architectural rule:

Server State → TanStack Query

Application State → React Context


Avoid Multiple Sources of Truth

Duplicating server data into client-side stores creates synchronization problems.

Example:

Student List

Stored In:

  • TanStack Query
  • Zustand

The application now has two sources of truth.

As data changes, synchronization logic becomes necessary.

This increases complexity and introduces bugs.

To avoid this, API data must never be copied into separate frontend state stores.


Simplicity

React Context is sufficient for the current application state requirements.

Examples:

  • Active School
  • Theme
  • Navigation State

Introducing additional state management libraries would increase the number of architectural concepts without providing significant value.


Modern Server State Management

TanStack Query provides:

  • Query Caching
  • Background Refetching
  • Automatic Retries
  • Query Invalidation
  • Loading States
  • Error Handling
  • Pagination Support

These features would otherwise require significant custom implementation.


State Ownership Rules

TanStack Query

Owns:

  • Students
  • Admissions
  • Staff
  • Attendance
  • Fees
  • Academic Data
  • School Data
  • User Data retrieved from APIs

Examples:

["students"]
["student", studentId]
["admissions"]
["fees", studentId]

TanStack Query is the source of truth for all backend-managed data.


React Context

Owns:

  • Active School Context
  • Theme
  • Sidebar State
  • Session UI Preferences

Examples:

SchoolContext
ThemeContext
NavigationContext

React Context is the source of truth for browser-only application state.


School Context Management

The frontend maintains an Active School Context.

Example:

User ↓ School Memberships ↓ Select School ↓ Active School Context

A user may belong to multiple Schools.

The frontend must provide a School Switcher within the application shell.

When the active School changes:

  • Context is updated
  • Queries are invalidated
  • Permissions are refreshed
  • Screen data is reloaded

Query Strategy

All API communication must use TanStack Query.

Example:

Student List

useQuery({
queryKey: ["students", schoolId],
queryFn: getStudents
})

The School Context must be included in query keys when applicable.

This ensures proper cache isolation between Schools.


Mutations

All data modifications must use TanStack Query mutations.

Example:

Create Student

useMutation({
mutationFn: createStudent,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["students"]
});
}
})

Mutations are responsible for refreshing affected queries.


Alternatives Considered

Redux

Pros:

  • Single state mechanism
  • Mature ecosystem

Cons:

  • Significant boilerplate
  • Server state concerns become application responsibilities
  • Increased complexity for MVP

Decision:

Rejected.


Redux Toolkit + RTK Query

Pros:

  • Modern Redux approach
  • Strong ecosystem

Cons:

  • Additional complexity
  • Reduced simplicity compared to TanStack Query

Decision:

Rejected.


Zustand

Pros:

  • Lightweight
  • Simple API

Cons:

  • Creates ambiguity regarding state ownership
  • Risk of duplicating server state
  • Additional architectural concept

Decision:

Deferred.

May be introduced later if application state becomes significantly more complex.


Consequences

Positive

  • Clear ownership of state
  • Reduced boilerplate
  • Excellent API caching
  • Easy onboarding for developers
  • Simplified architecture
  • Strong support for school context switching

Negative

  • Multiple React Context providers may exist
  • Zustand may eventually be required if application state grows significantly

Implementation Notes

Example structure:

src

├── app
├── modules
├── shared
├── providers
│ ├── school-context
│ ├── theme-context
│ └── auth-context

└── api

TanStack Query should be configured centrally.

React Context providers should be registered at the application shell level.

Business entities must never be copied from TanStack Query into separate state stores.


Decision Summary

Chilarai will use:

  • TanStack Query for Server State
  • React Context for Application State

Server state and application state have distinct responsibilities and ownership boundaries.

This approach minimizes complexity, avoids duplicate sources of truth, and provides a simple, scalable frontend architecture.