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}`,
thisView on GitHub (pinned to 6c5d78ee04)
Solutions
- Mount to a normal container element: <div id="app"></div> with el: '#app'.
- If you need to render into body, append and mount a child wrapper element instead.
- 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
- Always mount to a dedicated container like <div id="app">
- Never pass 'body' or 'html' to el/$mount
- Check querySelector result is not body/html before mounting
- Add an app bootstrap assertion in dev
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
- Template element not found or is empty: ${options.template}
- Cannot find element: ${el}
- tag <${tag}> has no matching end tag.
- [Vue tip]: ${msg}
- passive and prevent can't be used together. Passive handler
AI-assisted analysis of Meituan-Dianping/mpvue@6c5d78ee04 (2026-09-02).
Data as JSON: /api/errors/f21f3a244cbbd352.
Report an issue: GitHub.