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))Bundle size for floppy-disk (opens in a new tab)
(4.4 kB gzip)
Bundle size for zustand (opens in a new tab)
(2 kB gzip)
Bundle size for @tanstack/react-query (opens in a new tab)
(11 kB gzip)
Bundle size for swr (opens in a new tab)
(5.3 kB gzip)
Dependencies0122
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();