Samuel
Bluesky’s social-app doesn’t use any global state manager (redux/zustand/jotai/mobx etc). Why is that? 🧵 an elaboration on this post:
Samuel@samuel.fm· 11 Aug 2025
the problem I have is that it changes global state from a last resort to a first resort. thus the lowest effort solution always turns into a nightmare mess instead of being isolated and composable
5 12 83
Samuel
What we do use: - plain old React context - @tanstack.com query Having used React context at scale… I have to say the react team was cooking once again. Yet another case of weird ergonomics pushing you to work in a good way. Hoisting state to context being a bit of a PITA is a feature, not a bug!
1 20
Samuel
Adding more and more to an enormous app means by and large, stuff gets implemented in the lowest friction way. This is where composition shines. You want state to be as local as humanly possible, so it doesn’t get everywhere.
1 14
Samuel
Global state managers on the other hand have the opposite effect. The lowest friction path becomes making your stuff global. This does not scale well at all, and becomes increasingly incomprehensible.
1 15
Samuel
@samuel.fm
The key with using Context correctly is to only put one thing in them at a time. Got two things? Use two contexts. The danger zone is when you start putting large bundles of stuff in there, like you would a global state manager. This is obviously gonna make your whole app rerender.
2 16
Samuel
My heuristic is to only bundle stuff into an object when it’s stuff that isn’t gonna change: functions, for example. We have a useNonReactiveCallback hook which is great for this. This means you’re not gonna cause a rerender.
1 8
Samuel
Then if you’ve got values that might change, put em in their own context. This means they won’t trigger rerenders of anything that doesn’t need them. I messed this up recently: the controls that opens the dialogs has the functions in an object from context. The mistake was I got lazy and needed…
1 6
Samuel
to also pass down an internal value to another component, so I stuck it in the same object. This value changes every time you open a dialog, so even though it’s only *used* in one place it gets *consumed* in many many places, triggering unnecessary renders. This is such an easy fix: just move it out
1 6
Samuel
Conclusion: react context is good actually and pushes you towards good code. You just need a bit of hygiene when using them: mostly just avoid bundling stuff together! pro tip: add a displayName to your contexts so you can tell wtf is happening in your profiler
2 1 26
Samuel
is it a footgun? sorta. but with react context, it's usually a case of "you're holding it wrong". with global state managers, it's impossible to *not* hold it wrong. don't make stuff global!
3 16
Safwan Parkar
so if i understood this right, the idea is to keep state local to each "component"? by component i don't necessarily mean the actual `.tsx` file, but just the particular module or something. i'm still figuring out frontend lol
1 2
Samuel
well - if you need to share data between components, lift it out of the component to their shared parent. most of the time you want to keep it as “low” as possible, and only move it to the top of the tree as a last resort. this keeps things simple
1 5
Jack
This is really insightful. I mostly agree! But I wouldn't write off global state entirely. I wonder why you think it's impossible to hold it wrong, even with global constructs like sessions, local storage, themes and syncing things like globalThis state to the app? 🤔 What do you think?
1 1
Samuel
but you can use react context for these things, you don’t need libraries for this. or useSyncExternalStore. these libraries reduce friction in the wrong places
1 2
Jack
Hmmmm thanks that's true. I'm gonna try it out. I've never used useSyncExternalStore before 🫣. I use Zustand but each store is tiny and I only have three of them. 🤣 I'll give removing Zustand a go and see how I feel.
1 1
Samuel
if it works for you don’t let me stop you! if you keep your stores small you’re probably using it right! I’ve just seen some awful zustand code in my time 😥
1 1
Jonny Buchanan
This is why Jotai feels like an odd-one-out on this list to me, straight to an atomic piece of data, no stores (as in collections of data) or slices or actions, zero ceremony around adding that one thing you need here and here and here.
Beders
That really depends on your state manager. Our apps use global state just fine. It’s a tree managed by controllers. When changes occur Re-Frame/Reagent know exactly which components should re-render. Doesn’t matter where in the VDOM. Views subscribe to the values they are interested in.
Luca Schneider
Interesting thoughts. How would you handle code that most of the times updates together but there are only certain components interested in the state of it (e.g. a user and we want to show the profile picture in the header and the user name somewhere else and his friends also somewhere else)?
Samuel
it’s true. I don’t really have an answer for that beyond “dont do that” maybe solvable via lint rule?
1
Alvar Lagerlöf
Lint rules that apply some of the time are kinda hard. Warnings are largely non-effective, and an error that semi-regurarly has to be surpressed isn't great. Unless it can be defined clearly ofc.
1 1
Alvar Lagerlöf
Just trying to be the devil's advocate, because I feel like "don't do that" isn't a good PR reject comment. Then I've gotta come with an alternate approach, and "split it into one context for each little thing" could easily cause a mess. But maybe that's a skill issue?
1 1
Samuel
totally agree. I don’t know if a lint rule here would be helpful or even possible