Browse documentation

ComwitProvider

Wrap the client tree once. The provider owns the model instances and query cache; each model initializes when a hook or action first accesses it.

'use client'

import { ComwitProvider, keepPreviousData } from '@comwit/state'
import { useRouter } from 'next/navigation'
import type { ReactNode } from 'react'

export function Providers({ children }: { children: ReactNode }) {
  const router = useRouter()

  return (
    <ComwitProvider
      context={{ router }}
      defaultOptions={{
        query: {
          staleTime: 30_000,
          gcTime: 5 * 60_000,
          placeholderData: keepPreviousData,
        },
        persist: { debounceMs: 100 },
        local: { database: 'my-app' },
      }}
    >
      {children}
    </ComwitProvider>
  )
}

Only children is required. <ComwitProvider>{children}</ComwitProvider> is a complete setup.

PropPurpose
contextShared values available to action factories, such as the router or auth helpers
defaultOptions.queryGlobal staleTime, gcTime, and placeholderData
defaultOptions.persistGlobal persistence debounce interval
defaultOptions.localIndexedDB database, fallback scope, and storage error handler
defaultOptions.interceptorsInterceptors applied to every action method

Query defaults can be overridden on a query field or by an imperative .query(arg, options) call. Selector .load(arg) only takes the query argument.

Use shared context in actions

import { action } from '@comwit/state'

type AppContext = { router: { push(href: string): void } }
type NavigationActions = { openProducts(): void }

export const navigationActions = action<NavigationActions, AppContext>(({ context }) => ({
  openProducts() {
    context.router.push('/products')
  },
}))

Context values update with provider renders; read them when the action runs when they may change over time.

Global interceptors

import { ComwitProvider, Log, OnError } from '@comwit/state'
;<ComwitProvider
  defaultOptions={{
    interceptors: [OnError((error) => reportError(error)), Log('info')],
  }}
>
  {children}
</ComwitProvider>

Execution order is provider → class → method → action body. See Decorators for retries, authorization, queues, and validation.

Isolation and local storage scopes

Nested providers own independent state. A new provider starts a new in-memory registry. For provider-level user or tenant scopes, remount the provider when the scope changes:

<ComwitProvider key={user.id} defaultOptions={{ local: { scope: `user:${user.id}` } }}>
  {children}
</ComwitProvider>

A collection can instead define its own static or model-derived scope, which takes precedence over the provider fallback. See local() scope rules.