Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/christianbaroni/stores/llms.txt

Use this file to discover all available pages before exploring further.

Base stores are the simplest and most foundational store type in @storesjs/stores. They hold synchronous, local state and expose explicit methods for reading and updating that state. Every other store type — derived, query, and virtual — can read from base stores and react to their changes. If you have UI preferences, form inputs, filters, or any piece of state that lives entirely on the client, a base store is the right tool.

Creating a Base Store

Use createBaseStore with a state creator function. The creator receives set, get, and store, and returns the initial state object.
import { createBaseStore } from '@storesjs/stores';

type Settings = {
  currency: string;
  locale: string;
  setCurrency: (currency: string) => void;
  setLocale: (locale: string) => void;
};

export const settingsStore = createBaseStore<Settings>(set => ({
  currency: 'USD',
  locale: 'en-US',
  setCurrency: currency => set({ currency }),
  setLocale: locale => set({ locale }),
}));
The state creator arguments behave as follows:
  • set — Updates store state. By default it performs a partial merge, so you only need to provide the keys you want to change. Calling set(state => ({ currency: 'EUR' })) leaves all other keys intact. Pass replace: true as the second argument to perform a full replacement instead (see setState behavior below).
  • get — Returns the current state synchronously. Useful inside action methods when you need to read state before computing an update.
  • store — The raw store object. Rarely needed, but available for advanced use cases like subscribing to state changes inside another action.
Here is a more complete real-world example drawn from the example app’s favorites store:
import { createBaseStore, time } from '@storesjs/stores';

type FavoritesState = {
  favorites: Record<string, Film>;
  addFavorite: (film: Film) => void;
  removeFavorite: (id: string) => void;
};

export const useFavoritesStore = createBaseStore<FavoritesState>(
  set => ({
    favorites: {},
    addFavorite: film =>
      set(state => ({ favorites: { ...state.favorites, [film.id]: film } })),
    removeFavorite: id =>
      set(state => {
        const { [id]: _, ...rest } = state.favorites;
        return { favorites: rest };
      }),
  }),
  { storageKey: 'favorites' }
);

Using in React

In the React build, every store is also a callable hook. Pass a selector to subscribe to a specific slice of state:
import { useFavoritesStore } from './favoritesStore';

function FavoriteCount() {
  const count = useFavoritesStore(s => Object.keys(s.favorites).length);
  return <Text>{count} favorites</Text>;
}
The component re-renders only when the selected value changes. You can optionally pass a second argument — a custom equality function — to control when the component re-renders:
import { shallowEqual } from '@storesjs/stores';

const favorites = useFavoritesStore(s => s.favorites, shallowEqual);
The store object itself (useFavoritesStore, settingsStore, etc.) is stable across renders — it is created once and never changes. You can safely reference it in callbacks and effects without adding it to dependency arrays.

Using Outside React

The same store works perfectly outside of React — in utilities, services, or background logic:
// Read state synchronously
const { currency } = settingsStore.getState();

// Subscribe to a specific slice of state
const unsubscribe = settingsStore.subscribe(
  s => s.currency,
  (currency, prevCurrency) => {
    console.log('Currency changed from', prevCurrency, 'to', currency);
  },
  { fireImmediately: true }
);

// Clean up the subscription later
unsubscribe();
The subscribe overloads mirror the hook’s selector API. SubscribeOptions accepts equalityFn and fireImmediately.
Every base store exposes a getInitialState() method that returns the original state produced by the state creator before any updates have been applied. This is useful for implementing reset actions: set(store.getInitialState(), true).

setState Behavior

setState on a base store has two modes:
// Partial merge (default) — only updates the provided keys
settingsStore.setState({ currency: 'GBP' });

// Full replacement — replaces the entire state object
settingsStore.setState({ currency: 'GBP', locale: 'en-GB', setCurrency, setLocale }, true);
Using replace: true requires you to pass the complete state shape, including all action methods.

Exporting Actions with createStoreActions

Action methods in the state object are stable references — they do not change between renders or state updates. You can extract them into a standalone actions object for clean, imperative usage throughout your app:
import { createStoreActions } from '@storesjs/stores';

export const settingsActions = createStoreActions(settingsStore);

// Anywhere in your app, no hook required:
settingsActions.setCurrency('EUR');
settingsActions.setLocale('fr-FR');
This pattern is especially useful in event handlers, background tasks, or tests where you do not want or need a React context. From the example app’s search store:
export const useSearchStore = createBaseStore<SearchState>(set => ({
  query: '',
  setQuery: q => set({ query: q }),
}));

// Stable action reference — safe to import and call anywhere
export const { setQuery } = useSearchStore.getState();

Persistence

Base stores support optional persistence out of the box. Provide a storageKey in the options object to opt in:
export const settingsStore = createBaseStore<Settings>(
  set => ({ currency: 'USD', locale: 'en-US', setCurrency, setLocale }),
  {
    storageKey: 'user-settings',
    partialize: state => ({ currency: state.currency, locale: state.locale }),
    version: 1,
    migrate: (persisted, version) => {
      if (version < 1) return { ...persisted, locale: 'en-US' };
      return persisted;
    },
  }
);
Key persistence options at a glance:
OptionDescription
storageKeyUnique key used to read/write from storage
partializeFunction returning the subset of state to persist
versionSchema version number (default 0)
migrateCalled when a version mismatch is detected
storageCustom storage adapter (sync or async)
For the full persistence guide including async storage, custom adapters, and hydration callbacks, see Persistence.

Build docs developers (and LLMs) love