ilokesto

Utility

@ilokesto/state/utils exports small helpers for the parts of state management that are not tied to a framework adapter:

  • pipe creates an @ilokesto/store instance and applies middleware in order.
  • adaptor wraps Immer produce so object updates can be written with draft mutation syntax.

Use Utility pages when you are building the store before connecting it to React, Vue, Svelte, Solid, or Angular.

Why utilities are separate from adapters

Framework adapters answer “how does this state become reactive in my UI?” Utilities answer “how do I prepare or write the state itself?” That separation lets the same store setup be reused across multiple frameworks or outside UI code.

import { create } from '@ilokesto/state/react';
import { logger, persist } from '@ilokesto/state/middleware';
import { pipe } from '@ilokesto/state/utils';

const preferencesStore = pipe(
  { theme: 'system' as 'system' | 'light' | 'dark' },
  persist({ local: 'preferences' }),
  logger({ collapsed: true }),
);

export const usePreferences = create(preferencesStore);

The component still uses the normal adapter API. The utility layer only prepares the store passed into create.

Installation notes

pipe only needs @ilokesto/state and its dependency on @ilokesto/store.

adaptor uses immer, which is an optional peer dependency. Install it only if you use adaptor.

pnpm add immer

Choose the right utility

UtilityUse it whenAvoid it when
pipeYou want to create a store and apply middleware left-to-right.You already own a Store<T> and want to mutate that exact instance manually.
adaptorObject updates are easier to express as draft mutations.State is primitive, or you do not want to install immer.

Typical composition flow

  1. Define initial state.
  2. Compose middleware with pipe if the state needs persistence, logging, validation, debouncing, or DevTools.
  3. Pass the resulting store to a framework adapter’s create.
  4. Use adaptor inside setState calls only where draft syntax improves readability.
import { create } from '@ilokesto/state/react';
import { validate } from '@ilokesto/state/middleware';
import { adaptor, pipe } from '@ilokesto/state/utils';

const profileStore = pipe(
  { name: '', tags: [] as string[] },
  validate(profileSchema),
);

const useProfile = create(profileStore);

function AddTagButton({ tag }: { tag: string }) {
  const [, setProfile] = useProfile((state) => state.tags);

  return (
    <button
      onClick={() =>
        setProfile(adaptor((draft) => {
          draft.tags.push(tag);
        }))
      }
    >
      Add tag
    </button>
  );
}

On this page