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()
Usage:
import { myStore } from './store'
myStore.value = 42
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())
// ...
})
Or using useLocalObservable
:
import { useLocalObservable, observer } from 'mobx-react-lite'
const MyComponent = observer(() => {
const store = useLocalObservable(() => new MyStore())
// ...
})
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!
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())
// ...
})
Example with useLocalObservable
import { useLocalObservable, observer } from 'mobx-react-lite'
const MyComponent = observer(() => {
const store = useLocalObservable(() => new MyStore())
// ...
})
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)