Comparison
Feature Comparison
Legend
β
: Supported
πΆ : Not built-in, but we can make our own
π΄ : Not officially supported or documented
Floppy Disk @2.13.0 | Zustand @4.4.7 | TanStack Query @5.12.2 | SWR @2.2.4 | |
---|---|---|---|---|
Bundle size (https://pkg-size.dev (opens in a new tab)) | (opens in a new tab) (4.4 kB gzip) | (opens in a new tab) (2 kB gzip) | (opens in a new tab) (11 kB gzip) | (opens in a new tab) (5.3 kB gzip) |
Dependencies | 0 | 1 | 2 | 2 |
TypeScript | β | β | β | β |
Create store | β | β | ||
- Selecting state slices | β | β | ||
- Async actions | β | β | ||
- Read/write state outside component | β | β | ||
- Subscribe changes outside component | β | β | ||
- Subscribe with selector | β | β | ||
- Without React | β | β | ||
- Transient updates | β | β | ||
- Using Immer | β | β | ||
- Middleware | β | β | ||
- Persist middleware | πΆ | β | ||
- Immer middleware | πΆ | β | ||
- Omit state key | π΄ [1] | β | ||
- Redux devtool | π΄ | β | ||
- Intercept set state | β | πΆ | ||
- Silently update state | β | π΄ | ||
- Store event handler Β ( onSubscribe , onUnsubscribe , etc.) | β | π΄ | ||
Create stores | β | π΄ | ||
Queries & Mutations | β | β | β | |
- SSR/SSG support | β | β | β | |
- React Suspense | β | β | β | |
- Cache persistence | β | β | β | |
- Parallel queries | β | β | β | |
- Dependent queries | β | β | β | |
- Lazy queries | β | β | β | |
- Lagged queries | β | β | β | |
- Paginated queries | β | β | β | |
- Infinite queries | β | β | β | |
- Bi-directional infinite queries | β | β | πΆ | |
- Infinite queries refetching | β | β | β | |
- Prefetching | β | β | β | |
- Retrying | β | β | β | |
- Stale while revalidate | β | β | β | |
- Initial data | β | β | β | |
- Scroll recovery | β | β | β | |
- Render optimization | β | β | β | |
- Cache manipulation | β | β | β | |
- Optimistic updates | β | β | β | |
- Selectors | β | β | π΄ | |
- Partial query matching | β | β | πΆ | |
- Window focus refetching | β | β | β | |
- Network status refetching | β | β | β | |
- Polling/intervals | β | β | β | |
- Query cancellation | πΆ | β | π΄ | |
- Offline mutation support | πΆ | β | π΄ | |
- Auto garbage collection | β | β | π΄ | |
- Does not need provider wrapper | β | π΄ | β | |
- Read/write query/mutation state outside component | β | β | π΄ | |
- Subscribe query/mutation state changes outside component | β | β | π΄ | |
- Devtool | π΄ | β | β |
Notes:
[1] Do we really need that feature? We can just set a state slice to undefined
.
DX Comparison
import { createStore } from 'floppy-disk';
export const useMyStore = createStore(
({ set, get }) => ({
count: 3,
shape: 'square',
increment: () => set((prev) => ({ count: prev.count + 1 })),
changeShape: () => {
const { shape } = get();
let nextShape;
if (shape === 'square') nextShape = 'circle';
else if (shape === 'circle') nextShape = 'triangle';
else nextShape = 'square';
set({ shape: nextShape });
},
})
);
function MyComponent() {
// Subscribe all changes
const { count, shape } = useMyStore();
// Subscribe to single slice
const count = useMyStore('count');
const shape = useMyStore('shape');
// Subscribe to multiple slices
const { count, shape } = useMyStore(
(state) => [state.count, state.shape]
);
...
}
// Get/set state outside component
const getShape = () => useMyStore.get().shape;
const increment = () => useMyStore.set((state) => ({ count: state.count + 1 }));
const usePokemonDetailQuery = createQuery(fetchPokemonById);
function PokemonDetailPage({ id }) {
const { isLoading, data, error } = usePokemonDetailQuery({ id });
if (error) return <div>Something went wrong</div>;
if (isLoading) return <div>Loading...</div>;
return (
<main>
<h1>{data.name}</h1>
<PokemonAttribute id={id} />
</main>
);
}
function PokemonAttribute({ id }) {
const { data } = usePokemonDetailQuery({ id });
return (
<section>
<div>Weight: {data.weight}</div>
<div>Height: {data.height}</div>
</section>
);
}
// Outside component
const prefetchPokemonById = ({ id }) => usePokemonDetailQuery.get({ id }).fetch();