ilokesto

Middleware

Store-level extensions for logging, persistence, validation, and debugging.

Middleware

@ilokesto/state middleware does not wrap React components. It wraps the underlying store. That is an important distinction, because these extensions change how updates flow through the store itself before React sees the result.

All built-in middleware are available from the @ilokesto/state/middleware entry point, and they are designed to compose cleanly with pipe().

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

const useStore = create(
  pipe(
    { count: 0 },
    logger(),
    persist({ local: 'my-app-storage' })
  )
);

Composition Pattern

Most middleware support two calling styles:

  1. Direct: middleware(store, options)
  2. Curried: middleware(options)(store)

The curried form is usually the best choice when you are composing a store declaratively.

Built-in Middleware

Each middleware does a different job at the store layer:

  • Logger: Logs state changes to the console.
  • DevTools: Connects your store to Redux DevTools.
  • Persist: Synchronizes state with localStorage, sessionStorage, or cookies.
  • Debounce: Batches rapid updates into one delayed flush window.
  • Validate: Validates state updates using Standard Schema v1.

Execution Order

When using pipe(), middleware are applied from top to bottom (left to right). That order is not cosmetic. It changes the update path.

For example, placing logger() before debounce() logs every attempted update, while placing it after logs only the delayed flush result.

If you only need one middleware, jump directly to its page. If you are composing several, think about ordering first and features second.

On this page