Skip to content

ViewModelsConfig configuration object ​

This configuration defines the behavior of ViewModel instances and the framework integrations.

The package provides a global object with this configuration. You can override supported options for a ViewModel or ViewModelStore with the vmConfig field.

ts
import { viewModelsConfig, ViewModelStoreBase } from "mobx-view-model";
import { withViewModel } from "mobx-view-model-react";

viewModelsConfig.comparePayload = false;

withViewModel(VM, View, {
  vmConfig: {
    comparePayload: false
  }
})

new ViewModelStoreBase({
  vmConfig: {
    comparePayload: false
  }
})

Reference to source code

Recommendations ​

These are the recommended settings for the global configuration viewModelsConfig, which contain the most optimal values.

ts
import { viewModelsConfig } from 'mobx-view-model';

viewModelsConfig.comparePayload = false;
viewModelsConfig.payloadComputed = 'struct';
viewModelsConfig.payloadObservable = 'ref';

viewModelsConfig.observable.viewModels.useDecorators = true; //false
viewModelsConfig.observable.viewModelStores.useDecorators = true; // false

mode ​

Runtime mode for the library integration:

  • 'csr-only' — (default) client-side only
  • 'ssr' — enable SSR-oriented behavior in the React / Solid integration

mode is global-only. Set it on viewModelsConfig before creating stores or view models.

resource ​

An optional default data source for the React and Solid integrations. Its read(id) method is called with the view model ID while the integration creates the view model. The returned value is available as vm.data on the view model (ViewModelBase initializes it from ViewModelParams.data).

read must return synchronously when used by Solid. React resources may also throw a Promise while loading or an Error, following the React Suspense resource convention. The resource is read on both the client and server when the view model is created. A store-level resource overrides this global default; see ViewModelStore.resource for a request-scoped example.

ts
import { viewModelsConfig } from 'mobx-view-model';

viewModelsConfig.resource = {
  read(id) {
    return cache.get(id);
  },
};

Use a store-level resource when the data must not be shared between requests:

ts
import { ViewModelStoreBase } from 'mobx-view-model';

const store = new ViewModelStoreBase({
  resource: {
    read(id) {
      return requestCache.get(id);
    },
  },
});

Do not create a mutable request-specific resource once at module scope in an SSR application. Construct it together with the store for each request.

getPayload ​

Extracts the ViewModel payload from component props.

Default: Returns allProps.payload or a shared frozen empty object when no payload is provided.

Used by withViewModel. Override globally or per HOC when you want a different payload shape (see also withPropsViewModel).

startViewTransitions ​

Controls view transitions for view model lifecycle moments.
MDN Reference

Shape ​

startViewTransitions is an object with these flags:

  • mount - start transition when the view mounts
  • payloadChange - start transition when the payload changes
  • unmount - start transition when the view unmounts

In ViewModelsRawConfig you can also pass a boolean to toggle all flags at once.

WARNING

This feature is experimental and not all browsers support it yet.

comparePayload ​

Allows you to configure how payload should be compared

payloadComputed ​

Allows you to configure computed statement of the payload

payloadObservable ​

Indicates type of observable for ViewModel payload.

factory() ​

Factory function for creating ViewModel / ViewModelSimple instances.

Can be helpful if you want to add some constructor arguments for your own ViewModel implementation

Default implementation

Creates ViewModelSimple with new VM(), and full ViewModel with new VM({ ...config, vmConfig: mergeVMConfigs(config.vmConfig) }). Store-level factories receive merged configuration; per-creation factories receive the raw creation config and must merge it themselves if needed. See global-config.ts.

Example ​

Passing RootStore as first constructor parameter

ts
import { viewModelsConfig } from "mobx-view-model";
import { rootStore } from "@/shared/store";

viewModelsConfig.factory = (config) => {
  const { VM } = config;
  return new VM(rootStore, config);
}

fallbackComponent ​

A component that will be rendered while the view model is in a loading or processing state.
This is useful for showing loading spinners, skeletons, or placeholder content.

Example ​

tsx
viewModelsConfig.fallbackComponent = () => (
  <div className="loading-spinner">
    Loading...
  </div>
);

reactHook ​

Optional default for the reactHook option on withViewModel: used when the HOC config does not set reactHook (resolved as config.reactHook ?? viewModelsConfig.reactHook).

onMount ​

A lifecycle hook that is called when a view model is mounted.
Useful for tracking component mounting, initializing external services, or setting up subscriptions.

Example ​

tsx
viewModelsConfig.onMount = (viewModel) => {
  console.log(`ViewModel ${viewModel.id} mounted`);
  // Setup analytics tracking
  analytics.track('component_mounted', { id: viewModel.id });
};

onUnmount ​

A lifecycle hook that is called when a view model is unmounted.
Useful for cleanup operations, removing subscriptions, or tracking component lifecycle.

Example ​

tsx
viewModelsConfig.onUnmount = (viewModel) => {
  console.log(`ViewModel ${viewModel.id} unmounted`);
  // Cleanup subscriptions
};

hooks ​

Internal event hooks for view model stores.

hooks.storeCreate ​

Called when a ViewModelStore instance is created.
Useful for wiring external listeners or diagnostics.

processRender ​

A higher-order function that processes and transforms the view render function before it is wrapped by withViewModel / Solid equivalent.
This function enables component composition and modification at the ViewModel level, allowing for:

  • Wrapping components with additional functionality (error boundaries, providers, etc.)
  • Injecting props or context
  • Modifying component behavior
  • Adding global features like logging, analytics, or performance monitoring

Example ​

tsx
import { observer } from "mobx-react-lite";

viewModelsConfig.processRender = (Component) => {
  // `withViewModel` wraps the *result* in `observer`; wrap the inner view
  // so `model` reads stay tracked when you add outer providers / boundaries.
  const Observed = observer(Component);
  return (props) => {
    return (
      <ErrorBoundary>
        <Observed {...props} />
      </ErrorBoundary>
    )
  }
}

This option is used by withViewModel in both React and SolidJS. The example above uses React's observer(); use the tracking mechanism for your framework when wrapping the render function. The callback also receives the ViewModel class and HOC configuration.

observable ​

This is a large configuration object for all base implementations in mobx-view-model, like ViewModelBase or ViewModelStoreBase.
Base implementations automatically apply MobX annotations using makeObservable(). Use useDecorators to choose the annotation style, or custom to provide your own annotation application function.

Properties of the nested observable configs:

- useDecorators ​

This changes the style of marking MobX annotations from "decorators style" to "non-decorators style".
Very helpful if you want to write code with "non decorators style".

This property has default value - true

Example:

ts
import { observable, action, makeObservable } from "mobx";
import {
  viewModelsConfig,
  ViewModelBase,
  ViewModelParams
} from "mobx-view-model";

viewModelsConfig.observable.viewModels.useDecorators = false;

class YourViewModel extends ViewModelBase  {
  constructor(params: ViewModelParams) {
    super(params);

    makeObservable(this, {
      fruitName: observable,
      setFruitName: action.bound,
    })
  }

  fruitName: string = '';

  setFruitName(fruitName: string) {
    this.fruitName = fruitName;
  }
}

Another example with "decorators style":

ts
import { observable, action } from "mobx";
import {
  viewModelsConfig,
  ViewModelBase,
} from "mobx-view-model";

viewModelsConfig.observable.viewModels.useDecorators = true;

class YourViewModel extends ViewModelBase  {
  @observable
  fruitName: string = '';

  @action.bound
  setFruitName(fruitName: string) {
    this.fruitName = fruitName;
  }
}

- custom(context, annotationsArray) ​

Custom function for wrapping your entity

global configuration object ​

You can override default global config using import viewModelsConfig.

ts
import { viewModelsConfig } from "mobx-view-model";

You should do this before the app starts.

Usage ​

ts
import { viewModelsConfig } from "mobx-view-model";

// Configure payload update/reactivity behavior
viewModelsConfig.payloadObservable = 'ref';
viewModelsConfig.comparePayload = false;
viewModelsConfig.payloadComputed = 'struct';


// Disable view transitions
viewModelsConfig.startViewTransitions = {
  mount: false,
  payloadChange: false,
  unmount: false,
};

// Optional configurations (uncomment to use)
// viewModelsConfig.factory = (config) => new config.VM(rootStore, config);
// viewModelsConfig.fallbackComponent = () => <LoadingSpinner />;
// viewModelsConfig.onMount = (vm) => console.log('Mounted:', vm.id);
// viewModelsConfig.onUnmount = (vm) => console.log('Unmounted:', vm.id);

Possible causes of infinite re-renders due to payload access ​

The flexible configuration of the payload reactivity and update behavior can lead to infinite re-renders inside the View component.
This happens when the payload is changing every time the component is re-rendered.

The following ViewModel configurations can cause this problem:

json
{
  "wrapViewsInObserver": true,
  "payloadComputed": true,
  "comparePayload": "shallow",
  "payloadObservable": "deep"
}
json
{
  "wrapViewsInObserver": true,
  "payloadComputed": true,
  "comparePayload": false,
  "payloadObservable": "deep"
}
json
{
  "wrapViewsInObserver": true,
  "payloadComputed": true,
  "comparePayload": false,
  "payloadObservable": "ref"
}
json
{
  "wrapViewsInObserver": true,
  "payloadComputed": false,
  "comparePayload": "shallow",
  "payloadObservable": "deep"
}
json
{
  "wrapViewsInObserver": true,
  "payloadComputed": false,
  "comparePayload": false,
  "payloadObservable": "deep"
}
json
{
  "wrapViewsInObserver": true,
  "payloadComputed": false,
  "comparePayload": false,
  "payloadObservable": "ref"
}
json
{
  "wrapViewsInObserver": false,
  "payloadComputed": true,
  "comparePayload": "shallow",
  "payloadObservable": "deep"
}
json
{
  "wrapViewsInObserver": false,
  "payloadComputed": true,
  "comparePayload": false,
  "payloadObservable": "deep"
}
json
{
  "wrapViewsInObserver": false,
  "payloadComputed": true,
  "comparePayload": false,
  "payloadObservable": "ref"
}
json
{
  "wrapViewsInObserver": false,
  "payloadComputed": false,
  "comparePayload": "shallow",
  "payloadObservable": "deep"
}
json
{
  "wrapViewsInObserver": false,
  "payloadComputed": false,
  "comparePayload": false,
  "payloadObservable": "deep"
}
json
{
  "wrapViewsInObserver": false,
  "payloadComputed": false,
  "comparePayload": false,
  "payloadObservable": "ref"
}