In this talk, I will talk about how I joined the development of Zustand. It started with another library of mine, which is similar to Zustand. My involvement was from Zustand v3 and it's currently v4. Philosophy of Zustand is being small, and we improve it with keeping the philosophy. Finally, we compare Zustand with Jotai, which is another state management library that I develop.
Development History of Zustand
This talk has been presented at React Summit 2023, check out the latest edition of this React Conference.
FAQ
Zestand is unique because it does not use React context for state propagation, which is common in many other state management libraries. This approach avoids the complexity and overhead associated with context providers, especially in applications where global state is defined at the module level.
The latest version of Zestand, V4, incorporates the useSyncExternalStore hook from React 18, improving concurrent rendering capabilities. It also features enhanced TypeScript types and has undergone significant codebase revisions for better performance and usability.
Contributors can get involved by improving documentation, adding tests, enhancing existing middleware, and developing third-party libraries to expand the Zestand ecosystem. The project is particularly open to contributions that enhance developer experience and documentation.
Future plans for Zestand include dropping support for React 17 and removing the useSyncExternalStore shim to streamline the library in line with newer React versions. The focus will remain on improving existing features rather than adding new ones, with a strong emphasis on concurrency support and stability.
Zestand and Jotai differ primarily in their state management models. Zestand uses a store with actions and selectors to manage state, whereas Jotai uses a more React-oriented approach with atoms that represent pieces of state and can be combined or derived. Jotai aims to avoid the need for selectors by using dependency tracking to optimize re-renders.
Zestand was initially released in April 2019 and has been developed by a team including Daishi Kato, who joined the development team in August 2020 to enhance its features, particularly for concurrent rendering in future versions of React.
Zestand is a state management library for React that focuses on providing a minimal API and small bundle size. Its primary purpose is to manage global state in React applications, following Flux principles and providing a less opinionated, yet extensible, approach.
1. Introduction to Zestand#
This talk is about the development history of Zestand and my involvement in it. Zestand is a state management library for React. I'm Daishi Kato, a freelancer who likes programming and open source software. Zestand is one of my successful projects. It got attention and became popular. React hooks inspired me to develop various libraries, including Zestand.
Hello, this talk is about the development history of Zestand and how I was involved in it. By the way, I pronounced it Zestand, but how we should pronounce it is not defined. We can call it as we like.
For those who don't know, Zestand is a state management library for React.
To begin with, let me introduce myself. I'm Daishi Kato. I'm a freelancer who likes programming, and especially open source software. There are lots of open source work I have done. Mostly started as experiments. Some of them got attention and become fairly known projects. Zestand is the library we talked today. Jyotai is another state management library which I gave a talk previously. Valusio is yet another library. All those three are developed with a team called Poimanderes. That last one is called Reactract, which is one of my big projects exploring React hooks. There are many other small libraries. Most of my recent work is with React hooks. But I have a broader interest in JavaScript in general.
Anyway, today's talk is about Zestand. Zestand is one of the successful projects. It's a state management library, and there are so many such libraries out there. Because there are so many, letting people know and getting popular is a huge challenge. Luckily, Zestand got some attention by now. It's just lucky, I suppose. I wasn't involved in the original development of Zestand, but I was doing something very similar back in the days. It was back in 2018. React announced the hooks at React Conf 2018 October. It was a great inspiration and I developed various libraries with hooks, some of which have been for state management. Before hooks, I had developed a library with React context, just like others did. I took the same approach and created a hooks version of it.
2. Development and Maintenance of Zastand#
While developing Zastand, I realized it didn't need React context for global state. I released the initial version in April 2019, and it gained some users. Zastand stood out for not using React context, unlike other global state libraries. I joined the team in August 2020 to make Zastand more concurrent rendering friendly. After version 2, Zastand wasn't maintained, so I took over and released version 3 with improvements.
While I was developing it, I knew it doesn't require React context because the use case is global state. Users will never have nested provider components. I actually made a version without context but it wasn't welcomed by users at the moment. And time passed.
It was April 2019 when the initial version of Zastand is released. It was still rough but got some users. With some collaborators, version 1 was released in June. Soon afterwards, I found it from somewhere. At that point, I had a tool, a repository, which is to compare various global state libraries. It was built to check the behavior of concurrent rendering. The tool was used for checking my libraries and some other popular libraries. So I added Zastand there too. Zastand was unique as it didn't use React context at all. All others used context for state propagation or scoping stores. I think React context has been overused for global state. Unless you use context providers multiple times in your component tree, it's not contextual. Global state defined at module level should just work. Zastand was a pioneer in this approach. I'm convinced with it and re-implemented one of my libraries. Later, I wrote a detailed blog post about it. Anyway, that's how I met Zastand. As I said, one of my repositories to compare various global states in concurrent rendering.
I was interested in making Zastand more concurrent rendering friendly for future React. I joined the team on August 2020 and took the development of Zastand. Zastand had been developed actively in the previous year and reached version 2. However, it wasn't actually maintained after that. There were a certain amount of users and few issues opened, but nobody was taking care of them. While my motivation was concurrent rendering, I started to maintain it and resolve those issues. v3 was released on the same month I started working on it. It had two big improvements, but other than that, it's basically compatible with the previous version.
3. Zestand Development and Future#
The next version of Zestand is focused on concurrent rendering and utilizes the useSyncExternalStore hook introduced in React 18. The core implementation of the library has been greatly improved, and the TypeScript types have been enhanced by a contributor. The project is currently stable, with some middleware and documentation still being worked on. Future versions will depend on the direction of React, and duplicate features will be removed. Zastand is a minimal global state library for React that follows Flux principles and prioritizes a small API and bundle size.
The good news is that I got familiar with the codebase. After the release, we fixed various small issues. As it got popular, it got more users, more contributors and more issues. We improved many small things, and more people were interested in trying.
At some point, we started preparing the next version. We did many experiments and duplicated some of old features. The next version is for concurrent rendering, which was my original motivation. By that time, React 18 was released, with a new hook called useSyncExternalStore. There was a long story behind it, but that hook is a successor to our user-run solution. Zest and V4 was released with that hook. It actually uses the shim library provided by React, so that it works with React 17. The library implementation is almost rewritten in V4 compared to what we had in V2 before I joined. The TypeScript types are improved pretty much, which is done by a contributor. It has been a great journey to reach here. I learned various things, not only coding, but also growing community. I appreciate everyone who involved in this project.
At this moment, the status of this project is fairly stable. Especially, the core implementation is basically finished. Some middleware like Persist has still room for improvement and we look for contributors. There is a huge demand to improve Docs and we are working on it. We will consider future versions once we understand how React moves forward. Especially, we are not sure about proposed use hook. It's still not certain what will come next. What will be done, for sure, is to drop duplicate features. We will probably remove the useSyncExternalStore shim. So the next major version, we'll likely drop React 17 support.
Let's back up a little bit and talk about what is Zastand. After the development, my conclusion is that Zastand is an answer to a question. The question is, what would be the smallest possible global state library for React? To add a little more requirements, it follows Flux principles, less opinionated and still extensible. So small API and small bundle size are the keys in Zastand.
4. Zastand: Small, Complete, and Growing#
Zastand is built on immutable states and aims to be small. The core implementation is complete, but bugs may still exist. Adding tests, Dev-only code, documentation, and improving middleware are encouraged. Zastand is small in terms of code size and offers a unique setState function. The ecosystem is growing with over 800 libraries depending on Zastand. Instead of adding features to the core library, third-party libraries are encouraged.
It's built on immutable states, as well as React is. In fact, creating a Zastand clone is a very easy task. You could add various features on top of it. Keeping it small and declining adding features is important for Zastand to be Zastand. Being small and not adding features means the current implementation almost defines the behavior.
Like I said, the core implementation is basically complete. That doesn't mean there are no bugs. There can be some bugs and we may change something in the future. The point is, we don't change it for more features. That brings to a ground rule about what kind of PRs are likely to be accepted. Adding tests to cover current behavior is welcome. Adding Dev-only code to improve developer experience would be great. Adding documentation is very helpful. TypeScript type may still have room for improvement. Finally, adding features is done by middleware. There are some middleware in the Zastand repository. Improving them would be nice, other than that, we are unlikely to add new middleware. We suggest people to create 3rd party libraries to grow our ecosystem.
I kept saying Zastand is small, how small is it? Let's see it's JavaScript code. The screenshot shows the code from version 4.1.3. It's 40 lines in ESM build. This doesn't include TypeScript types, some utility functions and middleware. Again, it should be pretty easy to create Zastand-like library from scratch. Probably the most unique part in Zastand code is that the setState function. Its default behavior is to merge state object so that it won't override action functions defined at the top level of the state object. It's great to see Zastand ecosystem is growing. There seems to be over 800 libraries that depend on Zastand. There are quite a few third-party libraries for Zastand, and the number continues to grow. As I said previously, we don't add new features in the core library. Instead, we encourage people to develop third-party libraries.
5. Monetizing Zastand and Exploring Product Options#
As Zastand gets popular, I'm faced with the challenge of monetizing my work. While most OSS can be used freely, I'm considering sponsorship, tutorials, courses, or books as potential revenue streams. My first attempt was a tweet with a code snippet, but I'm still exploring other options.
I hope more people get interested in it. Now, as Zastand gets popular, I got a little challenge for myself. The challenge is how to monetize this work. Most OSS can be used freely, so there is no direct reason to pay to use it. And it's good, because you can try it without any risks before deciding whether to use it or not. For monetizing OSS, one might think about sponsorship, which is good and works to some extent. Apart from sponsorship, I noticed some OSS developers sell tutorials, courses, or maybe books. It's more meaningful to pay for a product. I like the idea. This tweet is my first try. I wrote a small code snippet to show simplified Zastand with some code comments. I'm not totally sure if it was successful, but there were some things I learned. For example, people were more willing to watch videos. The challenge is not finished. I'm still trying to explore what products I can provide.
6. Introduction to Jotai#
Let's talk about Jotai, another state management library I developed, and compare it with Zastand. Jotai was developed after the release of Zastand version 3 with a focus on React orientation and concurrency support. One of the goals of Jotai is to avoid selectors and instead have dependency tracking. The state model in Jotai is different from Zastand, as it has multiple states that are combined to derive a new state, which are called atoms.
So much for the Zastand story. Let's talk a little bit about Jotai, another state management I developed and compare it with Zastand. While developing Zastand version 3, I have another idea that isn't very compatible with Zastand. It's more React oriented and concurrency support in mind. So we decided to develop Jotai after the v3 release. One of the goals of Jotai is to have dependency tracking that can avoid selectors. Selectors are necessary in Zastand to optimize re-renders. This makes the state model very different. While Zastand has a store and selectors create slices of store, Jotai has multiple states at first, and we combine them to derive a new state. We call them atoms.
7. Comparing Zastand and Jotai Implementations#
In this part, we compare the Zastand and Jotai implementations of a counter app. Zastand uses a store, counter component, and double component with a selector function. Jotai uses atoms, counter component, and doubled component. Jotai avoids selectors and re-evaluates functions when dependencies change.
Would you be interested in comparing Zastand and Jotai? To explain how these two are different when you develop apps, let's take a very simple example. A counter app. We will see the code in two implementations. It has one component that shows a count number and a button to increment the number. There's another component that shows a double count number. The double count is a derived state that automatically updates when the original count changes.
The first implementation is with Zastand. This shows the Zastand implementation of the counter app. There are three functions. The first one is to create a store. It's conventional to have actions in a store, so we have a function to increment the count value. The second is the counter component. It takes count value and ink function and renders a number and a button. The last one is double component. It uses a selector function to create a derived value. This is the primary method in Zastand to create derived states. It's fairly straightforward, and it is hook oriented. One pitfall with the selector function is if it creates a new reference, it will cause an infinite loop. And that's the motivation for Jotai to avoid selectors.
Let's move on and see the implementation with Jotai. This is the Jotai implementation of the counter op. We first define two atoms. One is called countAtom, which is a primitive atom or data source. The other is called doubleAtom, which is a derived atom. The derived atom is defined with a function. The function is re-evaluated when dependency changes. The following two components should seem familiar. The first one is the counter component, whose usage is the same as use state. The second one is the doubled component, which reads atom value and displays it. We could create an action atom for increment function as we did in Zastand.
8. Zastand vs Jyotai: State Models and Comparison#
We could omit actions in Zastand too. It's a difference in programming style. Zastand is a small library for global state in React. It's less opinionated and doesn't hold derived states. Jyotai, on the other hand, focuses on state abstraction and data graph. This talk covers the development history of Zastand and the comparison with Jyotai.
We could omit actions in Zastand too. So it's more or less a difference in programming style. I hope this illustrates the basic difference of state models between Zastand and Jyotai.
To summarize this talk, we have been developing Zastand for the past few years. Zastand is a small library to provide a store for global state for React. It's less opinionated and it has small assumptions. Zastand store can have actions, but it doesn't hold derived states. It is for a single source of truth.
We developed Jyotai for the same goal, but with a different state model. Jyotai atoms represent pieces of states. They can be derived from others. Actions can be defined in components, hooks, and also in atoms. Atoms are abstract definitions of states. To put it short, Zastan is more focused on store and actions while Jyotai is more focused on state abstraction and data graph. This talk covered from the development history of Zastan to the comparison with Jyotai. I hope it helps understanding global state libraries.
Comments