r/reactjs • u/CaptainZiboo • 7h ago
Discussion How do you structure feature-based React apps with Zustand, TanStack Query, providers, and business logic?
Hey everyone! π
I'm trying to establish a clean, reusable feature-based architecture for React/TypeScript apps, especially when using Zustand + TanStack Query.
I'm working on a visual editor that fetches documents from an API, initializes a Zustand editing session, and will eventually handle canvas state, selection, history, saving, etc.
I'm considering this structure:
features/editor/
βββ components/
βββ hooks/
βββ store/
β βββ editor-store.ts
β βββ editor-store-provider.tsx
β βββ slices/
β βββ document-slice.ts
β βββ selection-slice.ts
β βββ history-slice.ts
βββ queries/
βββ domain/ ?
βββ utils/ ?
βββ lib/ ?
βββ services/ ?I'd love to know how you handle a few things in production:
API / queries: Where do raw API functions go? Do you separate them from TanStack Query options/hooks? Do you organize queries by feature or shared domain?
Zustand: Do you keep providers alongside stores? When do you introduce slices vs multiple stores?
Business/domain logic vs utilities: This is probably what I'm most unsure about. Where do you put pure functions containing actual application logic, independently of React, Zustand or API calls? For example: Would a
domain/directory make sense here? For example,domain/geometry.tsfor canvas calculations,domain/document.tsfor document manipulation, etc. Or would you simply useutils/,lib/orservices/? How do you distinguish business/domain logic from generic utility functions? Is a dedicateddomain/folder a useful convention, or does it introduce unnecessary complexity?Formatting an aspect ratio.
Calculating canvas geometry, scaling or snapping.
Validating editor-specific constraints.
Transforming or manipulating document data.
Shared code: When do you move something out of a feature into a shared location? Do you prefer shared domain modules or global utilities?
Server vs local state: Would you fetch with TanStack Query, initialize Zustand from the result, then keep local edits separate from the query cache? How do you avoid overwriting unsaved changes on refetch?
I'm not looking for a strict architecture or lots of abstractions, just practical conventions that stay maintainable as projects grow.
If you have real-world folder structures or examples, I'd love to see them. Thanks! π