remix-run/remix · warning

render called after component was removed, potential applica

Error message

render called after component was removed, potential application memory leak

What it means

The UI component runtime warns when render() is invoked on a component that was already removed from the tree. This usually indicates a retained reference to a removed component whose render is still being scheduled, which leaks memory and does work that is discarded.

Source

Thrown at packages/ui/src/runtime/component.ts:298

  #scheduleUpdate = (): void => {
    let queue = this.#updateQueue
    if (!queue) throw new Error('scheduleUpdate not implemented')
    let vnode = this.#updateVNode
    let domParent = this.#updateDomParent
    if (!vnode || !domParent) throw new Error('scheduleUpdate target not initialized')
    queue.enqueue(vnode, domParent)
  }
  #tasks: Task[] = []

  constructor(config: ComponentConfig) {
    this.#config = config
    this.frame = config.frame
    this.#handle = this.#createHandle()
  }

  render = (nextProps: ElementProps): [RemixNode, Array<() => void>] => {
    if (this.#removed) {
      console.warn('render called after component was removed, potential application memory leak')
      return [null, []]
    }

    this.#abortRenderSignal()
    syncProps(this.#props, nextProps)

    let renderFn = this.#renderFn

    if (renderFn === undefined) {
      let initialize = this.#config.type as unknown as (handle: Handle<ElementProps, C>) => unknown
      let result = initialize(this.#handle)

      if (!isRenderFn(result)) {
        let name = this.#config.type.name || 'Anonymous'
        throw new Error(`${name} must return a render function, received ${typeof result}`)
      }

      renderFn = result

View on GitHub (pinned to 9696913134)

Solutions

  1. Cancel pending render schedules in the component's cleanup/unmount path
  2. Audit subscriptions and timers that capture the component and close over it after removal
  3. Return early when the component's owning region is unmounted

Example fix

// before
const interval = setInterval(() => component.render(props), 1000)
// after
const interval = setInterval(() => { if (!removed) component.render(props) }, 1000)
clearInterval(interval) // on cleanup
Defensive patterns

Strategy: validation

Validate before calling

if (!component.isRemoved?.()) component.render(props)

Prevention

When it happens

Trigger: Holding onto a component runtime handle (or a stale scheduler callback) after the component unmounted, then calling render(nextProps) on it.

Common situations: Missing cleanup in effects/subscriptions that capture the component; scheduler queues not flushed or cancelled on unmount; hydration regions replaced while queued renders remain.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/3249622668680ab0. Report an issue: GitHub.