mobxjs/mobx · warning

[mobx-react] It seems that a re-rendering of a React compone

Error message

[mobx-react] It seems that a re-rendering of a React component is triggered while in static (server-side) mode. Please make sure components are rendered only once server-side.

What it means

In static rendering mode (used for server-side rendering via useStaticRendering(true) or StaticRenderer), observer components should render exactly once with no subsequent updates. The class component's shouldComponentUpdate (observerSCU) detects a re-render attempt while static rendering is active and logs this warning — state/props updates after the initial SSR render indicate a lifecycle misuse. It is a console.warn, not a throw.

Source

Thrown at packages/mobx-react/src/observerClass.ts:239

            // This happens when component is abandoned after render - our reaction is already created and reacts to changes.
            // `componenDidMount` runs synchronously after `render`, so unlike functional component, there is no delay during which the reaction could be invalidated.
            // However `componentDidMount` runs AFTER it's descendants' `componentDidMount`, which CAN invalidate the reaction, see #3730. Therefore remember and forceUpdate on mount.
            admin.reactionInvalidatedBeforeMount = true
            return
        }

        try {
            admin.forceUpdate?.()
        } catch (error) {
            admin.reaction?.dispose()
            admin.reaction = null
        }
    })
}

function observerSCU(nextProps: ClassAttributes<any>, nextState: any): boolean {
    if (isUsingStaticRendering()) {
        console.warn(
            "[mobx-react] It seems that a re-rendering of a React component is triggered while in static (server-side) mode. Please make sure components are rendered only once server-side."
        )
    }
    // update on any state changes (as is the default)
    if (this.state !== nextState) {
        return true
    }
    // update if props are shallowly not equal, inspired by PureRenderMixin
    // we could return just 'false' here, and avoid the `skipRender` checks etc
    // however, it is nicer if lifecycle events are triggered like usually,
    // so we return true here if props are shallowly modified.
    return !shallowEqual(this.props, nextProps)
}

function createObservablePropDescriptor(key: "props" | "state" | "context") {
    return {
        configurable: true,
        enumerable: true,

View on GitHub (pinned to 01211a698b)

Solutions

  1. Ensure components are rendered only once server-side; remove setState calls during SSR
  2. Guard state updates with typeof window checks or move updates to componentDidMount (client-only)
  3. Call useStaticRendering(false) (or use enableStaticRendering(false)) for client bundles
  4. In tests, reset static rendering mode between suites (resetMobxReactTestState)

Example fix

// before
componentDidMount() { /* ok */ }
static rendering + this.setState(...) during render
// after
// only update on the client, after mount:
componentDidMount() { if (!isUsingStaticRendering()) this.setState(...) }
Defensive patterns

Strategy: validation

Validate before calling

// before mutating state, check static rendering
import { isUsingStaticRendering } from 'mobx-react'
if (!isUsingStaticRendering()) {
    this.setState({ ... })
}

Type guard

function canUpdateOnClient(): boolean {
    return typeof window !== 'undefined' && !require('mobx-react').isUsingStaticRendering()
}

Try / catch

null

Prevention

When it happens

Trigger: Calling setState, receiving new props, or otherwise triggering shouldComponentUpdate on an observer class component while isUsingStaticRendering() is true (server render or static-render test setup).

Common situations: Rendering components during SSR where parent code calls setState in render or lifecycle; running components under mobx's static rendering in tests but mutating state mid-render; accidental client-side use of useStaticRendering(true).

Related errors


AI-assisted analysis of mobxjs/mobx@01211a698b (2026-08-28). Data as JSON: /api/errors/a6bea27902bf7614. Report an issue: GitHub.