In Vue 3, what does app.mount() return, and why does createApp(App).mount('#app').use(plugin) fail?
answer
- registration methods return the app
- mount is the last call
- root component instance
- element or selector
- mount only once
basics
~20 sapp.mount() renders the root component into the container and returns the root component instance, not the app. That instance has no use() method, so chaining .use() after mount throws; configure and register everything first, then call mount last.
solid answer
~40 s`createApp(App, rootProps?)` only creates an application instance; nothing renders yet. Configuration and registration methods such as `use`, `component`, `directive`, `provide` and `mixin` all return that app, so they chain. `mount(container)` accepts a DOM element or a CSS selector, renders the root component inside it and returns the **root component instance** instead of the app. `createApp(App).mount('#app').use(plugin)` therefore calls `use` on the component instance, which has no such method, and fails with a TypeError. It is also backwards in spirit: the first render has already happened without the plugin. `mount()` belongs at the end of the chain, and it can only be called once per app.
code
ts · 12 linesimport { createApp } from 'vue'
import App from './App.vue'
import { i18n } from './plugins/i18n'
// Wrong: mount returns the root component instance, which has no use()
// createApp(App).mount('#app').use(i18n)
// Right: configure first, mount last
const app = createApp(App)
app.use(i18n)
const root = app.mount('#app') // root component instance
console.log(root.$el)go deeper
Recall that createApp builds the app, registration methods chain, and mount goes last and returns the root component instance.
Explain what mount does step by step, the first-match selector rule, and the warnings for missing targets and double mounts.
Structure bootstrapping so configuration is complete before the first render, and use factories when apps must be created repeatedly.
Treat the entry file as the app's composition root: a single ordered place where configuration, plugins and mounting are reviewed together.
## Two objects, two roles A Vue 3 application has two distinct objects that are easy to confuse: - the **application instance**, returned by `createApp(rootComponent, rootProps?)`. It holds app-wide configuration and registrations and renders nothing by itself; - the **root component instance**, created when the app is mounted. It is the live instance of the root component, the top of the component tree. `createApp` builds the first. `mount` creates the second and returns it. ## What mount does 1. It resolves the container: a DOM element is used as-is, and a selector string is passed to `document.querySelector`, so the **first** matching element wins. If nothing matches, development builds warn *Failed to mount app: mount target selector "#app" returned null.* and nothing is mounted. 2. It creates the root component's virtual node with the root props and attaches the app's context, which carries all configuration and registrations made so far. 3. It renders the tree into the container, running `setup` and the mount lifecycle of every component. 4. It returns the root component instance. ## Why the chain breaks | Method | Returns | Chainable with app methods? | |---|---|---| | `app.use(plugin)` | the app | yes | | `app.component(name, comp)` | the app | yes | | `app.provide(key, value)` | the app | yes | | `app.mount('#app')` | the root component instance | no | So `createApp(App).mount('#app').use(plugin)` evaluates `mount` first and then calls `use` on the component instance, which has no `use` method: a runtime TypeError. Even if it did not throw, the order would be wrong, because the first render ran before the plugin registered its components, directives or provides. The Vue guide's rule is direct: call `mount()` after all app configuration and asset registration is done. The correct shape keeps the app in a variable or puts mount last: ```ts const app = createApp(App) app.use(i18n) app.component('BaseIcon', BaseIcon) app.mount('#app') ``` ## Mount once per app An app instance can be mounted a single time. A second `mount()` call on the same app renders nothing and warns in development that the app has already been mounted, suggesting a factory function such as `const createMyApp = () => createApp(App)` if you need fresh instances. One side effect is easy to miss: the DOM layer empties the target container before that check runs, so a stray second mount can blank an element. Mounting a different app into a container that already hosts one also warns, asking you to `unmount()` the previous app first. ## What the returned instance is good for The return value is occasionally useful in plain-script integrations and debugging: it gives access to `$el` and to whatever the root exposes. A `<script setup>` root is closed by default, so its internal bindings are not reachable through that value unless the component exposes them explicitly. Application code should not depend on it heavily; state that outside code needs is better shared through a store or a provided service. ## What TypeScript tells you The types make the distinction visible before runtime. `createApp` returns an `App`, whose `use`, `component`, `directive`, `provide` and `mixin` methods are declared to return `this`, which is what makes chaining type-check. `mount` is declared to return a `ComponentPublicInstance`, which has no `use` member, so in a TypeScript project `createApp(App).mount('#app').use(i18n)` is a compile error long before it is a runtime TypeError. In plain JavaScript the mistake survives until the page loads, which is why it is a classic screening question: it tests whether the candidate knows two different objects are involved. A useful habit is to keep the app in a named variable, `const app = createApp(App)`, and put each configuration step on its own line. The entry file then reads as an ordered checklist, and a plugin added later cannot accidentally land after the `mount()` call. ## Common mistakes - Chaining app methods after `mount()`. - Registering a plugin or global component after mount and expecting the first render to have used it. - Passing a selector that matches several elements and expecting all of them to be mounted. - Calling `mount()` again to re-render; Vue re-renders reactively on its own, and a remount needs a new app. - Mounting before the container exists, for example from a script in the page head that runs before the body is parsed.
- What happens in Vue 3 if the mount selector matches no element?The DOM-specific `mount` resolves the selector with `document.querySelector`. When it returns null, development builds warn that the mount target selector returned null, and `mount` returns without rendering anything. The usual causes are a typo or running the script before the element has been parsed.
- If a selector matches three elements, how many apps are mounted?One app, on the first match only, because the selector goes through `querySelector`. To enhance every matching element, loop over `querySelectorAll` and create a separate app for each element, since each app instance can only be mounted once.
saying these in an interview costs you the question
- app.mount() returns the app instance, so calls can continue after it
- Plugins registered after mount apply to the first render as well
- A selector passed to mount() mounts the app on every matching element
- The same app instance can be mounted several times in different containers
- createApp() renders the root component immediately