Nanostores in SSR

Author: AlexandrHoroshihCreated Oct 25, 2021Updated Jan 29, 2026

Hi there!

I saw news on new release, saw allTasks API and decided to research, how nanostores is compatible with SSR

Loading of Node.js template in codesandbox takes forever for some reason, so i decided to create similiar reproduce with basic one: https://codesandbox.io/s/loving-wilson-4vp4r?file=/src/index.js (see index.js and console)

Idea is the same:

  1. Request happens
  2. Request-handler task is added to event-loop and starts some logic and web or db requests
  3. Once those web or db requests are over, state is recalculated with new data
  4. State injected in the app, which then rendered to string
  5. Resulting string sent as response

During all of these operations is important to isolate state that "belongs" to different requests from each other - otherwise data from one request will leak to the other

Nanostores have two issues there:

  1. States of nanostores are shared between requests, which then leads to data prepared for one request to show in another - in result rendered html-string is not correct.

  2. State of internal allTasks counter is also shared between all requests, which leads to weird bug: responses are not sent until all requests are handled, even if their data is ready.

Possible solutions:

  1. Many state-managers require creation of new instance of store/stores for every request, like this
typescript
const ssr = async (request) => {
 const stores = initStores()
 
 await stores.startLogic(request)
 
 return renderAppToString(stores)
}

This will effectively isolate requests from each other, because all of them will have their own instance of stores and all pending operations also will be bounded to this instance Mobx example: https://github.com/vercel/next.js/tree/master/examples/with-mobx-state-tree

  1. Some other state-managers, like effector, allow to create separate instance of app state in special way, allowing to reuse already initialized stores and connections, like this:
typescript
const ssr = async (request) => {
 // all stores and connections already defined somewhere
 const scope = fork()
 
 await allSettled(startLogic, { scope, params: request })
 
 return renderAppToString(scope)
}

Example: https://github.com/GTOsss/ssr-effector-next-example/blob/effector-react-form-ssr/src/pages/ssr.tsx