skip to content

In Vue 3, what does app.unmount() do, and can the same app instance be mounted again afterwards?

level: middleimportance: should knowfreq 28%

answer

  1. tears down the whole tree
  2. unmount hooks run
  3. container emptied
  4. mounted flag never resets
  5. factory for a fresh app

basics

~20 s

app.unmount() tears down the whole component tree, running every component's unmount hooks and clearing the container. The same app instance cannot be mounted again; create a fresh app, usually from a factory function, for each mount.

solid answer

~40 s

`app.unmount()` first runs any cleanup callbacks registered on the app, then renders nothing into the container, which unmounts the root and every descendant: their `onBeforeUnmount` and `onUnmounted` hooks run, reactive effects and watchers they own stop, and their DOM is removed. It also clears the marker Vue left on the container, so another app can mount there. What it does not do is reset the app for reuse: a second `mount()` on the same instance renders nothing; development builds warn that the app has already been mounted and suggest a factory. The pattern is `const createWidget = () => createApp(Widget)`, creating and configuring a new app for every mount.

go deeper

for a junior

Recall that app.unmount() removes the whole app and runs every component's unmount hooks.

for a middle

Explain the teardown order, why a mounted app cannot be remounted, and the factory function that creates fresh apps.

for a senior

Design mount and unmount lifecycles for widgets and micro-frontends so no watchers, listeners or plugin side effects outlive the app.

for a principal

Set conventions for embedding Vue in host applications: factories for creation, explicit teardown contracts, and ownership of cleanup.

## Why apps get unmounted at all Most single-page apps are mounted once and live until the tab closes. Explicit teardown matters when Vue lives inside something else: - a widget on a server-rendered page whose section is replaced by the page's own scripts; - a micro-frontend shell that mounts and unmounts sub-apps as the user navigates; - a modal or panel implemented as a separate app; - tests that create an app per test case. In all of these, forgetting to unmount leaks components, watchers and event listeners. ## What app.unmount() does 1. It runs any cleanup callbacks registered on the app, a hook added in Vue 3.5 that plugins mainly use. 2. It renders `null` into the container. Vue walks the whole component tree: for each component it runs `onBeforeUnmount`, stops the component's reactive effects, including its render effect, `watch` and `watchEffect` callbacks and `computed` values, removes its DOM and then runs `onUnmounted`. 3. It clears the marker Vue stored on the container element, so the element no longer reports a mounted app. Calling `unmount()` on an app that was never mounted does nothing except warn in development: *Cannot unmount an app that is not mounted.* ## Why the same app cannot be remounted An app instance keeps an internal mounted flag that `mount()` sets and `unmount()` never clears. After an unmount, calling `mount()` again renders nothing; the development build warns that the app has already been mounted and suggests moving app creation into a factory function. That is deliberate: an app accumulates registrations, provides and plugin state, and reusing it would carry state from the previous life into the next. | Goal | Do this | |---|---| | remove a widget | `app.unmount()` | | show it again later | build a new app with a factory and mount it | | move it to another element | unmount, then mount a new app on the new element | | mount a new app where one lives | `unmount()` the old app first; Vue warns otherwise | ## The factory pattern ```ts import { createApp } from 'vue' import Panel from './Panel.vue' import { i18n } from './plugins/i18n' export function createPanelApp(props: { orderId: number }) { return createApp(Panel, props).use(i18n) } let panel: ReturnType<typeof createPanelApp> | null = null export function openPanel(el: Element, orderId: number) { panel?.unmount() panel = createPanelApp({ orderId }) panel.mount(el) } ``` Each call produces a fully configured, fresh app, so no state survives from one mount to the next and every plugin installs cleanly. ## What unmount does not clean up - Timers, global listeners or subscriptions started **outside** components or in plugin install code, unless something registered teardown for them. - Module-level state imported by the app: it lives as long as the module does. - DOM outside the container, such as content a component moved elsewhere with its own code. Inside components, cleanup belongs in `onUnmounted` or in the `watch` cleanup callback; at app level, in teardown registered on the app. ## Verifying that teardown is complete - In a test, mount the app, unmount it, and assert that the container is empty and that `onUnmounted` callbacks fired. - In the browser, take heap snapshots after several mount and unmount cycles; detached DOM trees or a growing number of component instances point at something never unmounted, or at a listener still referencing it. - Trigger global listeners after unmount: if they still run, their registration was not paired with a removal in `onUnmounted`. A widget that passes these checks can be mounted and removed any number of times without the page growing heavier. ## Common mistakes - Removing the container element from the DOM without calling `unmount()`, leaving components, watchers and listeners alive. - Calling `mount()` again on an unmounted app and wondering why nothing renders. - Mounting a second app into an occupied container without unmounting the first. - Assuming unmount stops intervals started in plugin code without explicit cleanup.

  • What happens if you remove the container element from the page without calling app.unmount()?
    The DOM disappears, but Vue's component tree does not know it. Components never run their unmount hooks, their watchers and any listeners they attached to window or document stay active, and everything they reference stays in memory. Always call `unmount()` before, or instead of, removing the host element.
  • How does unmount interact with a second app mounted into the same container?
    Vue stores an internal `__vue_app__` property on a container that hosts an app. Mounting another app there while the first is mounted warns in development, asking you to unmount the previous app first. `unmount()` removes the marker, so a new app can then mount cleanly.

An app instance is like a single-use ticket: mounting punches it, unmounting ends the show, but the punched ticket will not get you in again. For another show you print a new ticket, which is what the factory function does.

saying these in an interview costs you the question

  • After app.unmount(), calling app.mount() again restores the app
  • Removing the container from the DOM is equivalent to app.unmount()
  • app.unmount() only removes the DOM without running component hooks
  • Unmount automatically stops timers started in plugin install code
  • Reusing one app instance across mounts keeps things faster and cleaner