DEV Community

Maxim Logunov
Maxim Logunov

Posted on

How to Properly Initialize MobX Store in React Applications with SSR

Introduction

MobX is a popular state management library for React applications. It allows you to store and modify data reactively, with components automatically updating when the state changes.

However, when using MobX with Server-Side Rendering (SSR, e.g., in Next.js), an important question arises: where and how to initialize the store?

The wrong approach can lead to state leaks between users, bugs, and even security issues.

In this article, we'll explore two main approaches to initializing MobX store, their pros and cons, and show how to work correctly with store in SSR applications.


Two Approaches to Initializing MobX Store

1. Singleton (Initialization in File)

The simplest and most common way is to create the store once and export it from a file:

// store.js
import { makeAutoObservable } from 'mobx'

class MyStore {
  value = 0
  constructor() {
    makeAutoObservable(this)
  }
}

export const myStore = new MyStore()
Enter fullscreen mode Exit fullscreen mode

Usage:

import { myStore } from './store'
myStore.value = 42
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Easy to import and use.
  • One store for the entire application.

Cons:

  • If the component is used multiple times, all instances share the same state.
  • Dangerous for SSR: state can leak between users!

2. Store Inside Component (useState, useMemo, useLocalObservable)

A more flexible approach is to create the store inside the component, ensuring each instance is isolated:

import { useState } from 'react'
import { observer } from 'mobx-react-lite'
import { MyStore } from './store'

const MyComponent = observer(() => {
  const [store] = useState(() => new MyStore())
  // ...
})
Enter fullscreen mode Exit fullscreen mode

Or using useLocalObservable:

import { useLocalObservable, observer } from 'mobx-react-lite'

const MyComponent = observer(() => {
  const store = useLocalObservable(() => new MyStore())
  // ...
})
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Each component gets its own store.
  • No conflicts between instances.
  • Safe for SSR: state won't leak between users.

Cons:

  • Need to pass the store to child components (via props or context).

Problems with Singleton Store in SSR

When using SSR (e.g., Next.js), the server can simultaneously render pages for different users.

If the store is created as a singleton, all users will work with the same store instance on the server!

What can go wrong:

  • One user sees another user's data.
  • State can "stick" between requests.
  • Leak of private data.
  • Difficulties with debugging and testing.

Example of a problem:

// store.js
export const myStore = new MyStore()
// ...
// Server renders a page for User A, changes myStore.value = 1
// At this moment, a request comes from User B, and they also get myStore.value = 1!
Enter fullscreen mode Exit fullscreen mode

Recommended Approach for SSR

Always create the store inside the component or at the page level!

Example with useState/useMemo

import { useState } from 'react'
import { observer } from 'mobx-react-lite'
import { MyStore } from './store'

const MyComponent = observer(() => {
  const [store] = useState(() => new MyStore())
  // ...
})
Enter fullscreen mode Exit fullscreen mode

Example with useLocalObservable

import { useLocalObservable, observer } from 'mobx-react-lite'

const MyComponent = observer(() => {
  const store = useLocalObservable(() => new MyStore())
  // ...
})
Enter fullscreen mode Exit fullscreen mode

Why is this safe?

  • Each render gets its own store instance.
  • No state overlap between users.
  • Easy to reset/initialize the store for each new request.

Practical Tips

  • Singleton can only be used for truly global state, which doesn't depend on the user (e.g., theme settings, features that don't change on the server).
  • Testing: create a new store for each test to avoid influence between tests.
  • Passing store down: use React Context or pass the store via props if child components need access to the state.
  • SSR and Hydration: if you need to pass initial state from server to client, serialize the store data and initialize the store on the client with this state.

Conclusion

MobX store in SSR applications must be isolated for each user and each render.

The singleton approach is dangerous and can lead to state leaks.

Create the store inside the component or page — it's simple, safe, and scalable.


Happy coding and clean state!


Note: This article assumes basic knowledge of React, MobX, and SSR concepts. For more detailed information, refer to the official documentation of MobX and Next.js.

Top comments (0)