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)) | (4.4 kB gzip) | (2 kB gzip) | (11 kB gzip) | (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();