Meituan-Dianping/mpvue · error

Do not mount Vue to <html> or <body> - mount to normal eleme

Error message

Do not mount Vue to <html> or <body> - mount to normal elements instead.

What it means

Vue refuses to mount an application instance onto <body> or <html>, because these elements host framework internals and external scripts; mounting would destroy/replace unrelated DOM. The instance init aborts (returns early) and a dev-mode warning is emitted.

Source

Thrown at src/platforms/web/entry-runtime-with-compiler.js:26

import { query } from './util/index'
import { shouldDecodeNewlines } from './util/compat'
import { compileToFunctions } from './compiler/index'

const idToTemplate = cached(id => {
  const el = query(id)
  return el && el.innerHTML
})

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)

  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }

  const options = this.$options
  // resolve template/el and convert to render function
  if (!options.render) {
    let template = options.template
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          template = idToTemplate(template)
          /* istanbul ignore if */
          if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Mount to a normal container element: <div id="app"></div> with el: '#app'.
  2. If you need to render into body, append and mount a child wrapper element instead.
  3. Use render functions with a proper container rather than body/html.

Example fix

// before
new Vue({ el: 'body', render: h => h(App) })
// after
new Vue({ el: '#app', render: h => h(App) })
// with <div id="app"></div> in the HTML
Defensive patterns

Strategy: validation

Validate before calling

function assertSafeMountTarget (el) {
  const resolved = typeof el === 'string' ? document.querySelector(el) : el
  if (resolved === document.body || resolved === document.documentElement) {
    throw new Error('Do not mount Vue to <html> or <body>; use a normal element')
  }
}
assertSafeMountTarget('#app')

Type guard

const isSafeMountEl = (el) => {
  const e = typeof el === 'string' ? document.querySelector(el) : el
  return !!e && e !== document.body && e !== document.documentElement
};

Prevention

When it happens

Trigger: Calling new Vue({ el: 'body' }) / new Vue({ el: 'html' }) or vm.$mount('body') / $mount('html') — query(el) resolves to document.body or document.documentElement.

Common situations: Beginners targeting 'body' as the app root; boilerplate written against older patterns; dynamically calling $mount on document.body.

Related errors


AI-assisted analysis of Meituan-Dianping/mpvue@6c5d78ee04 (2026-09-02). Data as JSON: /api/errors/f21f3a244cbbd352. Report an issue: GitHub.