A Vue 3 page imports createApp from 'vue' through a bundler and mounts a template-less root onto server-rendered markup, but the markup vanishes: what went wrong?
answer
- which file does 'vue' resolve to
- mount copies innerHTML into template
- container cleared before mount
- no compiler registered
- read the exact dev warning
basics
~20 sThe bundler's default 'vue' entry is the runtime-only build. mount() copies the container's HTML into the root's template and clears the container, but no compiler is registered, so nothing renders and a dev-only warning names the full build to use.
solid answer
~40 s`import 'vue'` in a bundler resolves to `vue.runtime.esm-bundler.js`, which has no template compiler. When the root component has neither `template` nor `render`, `app.mount()` copies the container's `innerHTML` into `template` and then clears the container. Setup cannot compile that template, so the render function stays a no-op and the page goes blank. In development Vue warns that the component provided a template option but runtime compilation is not supported in this build, and suggests aliasing `vue` to `vue/dist/vue.esm-bundler.js`; in production it fails silently. Fix it by using a build with the compiler, or by moving the markup into an SFC or render function. Also check that the region holds no user-provided content, since it will be executed as a template.
go deeper
Recall that the default bundler entry has no compiler, so a template living in the page cannot be compiled by it.
Walk through mount: innerHTML copied into template, container cleared, no compiler registered, render stays a no-op, dev warning names the fix.
Distinguish the two warnings, pick between switching builds and moving markup into SFCs, and flag the trust risk of compiling server-rendered, user-provided content.
Decide how a legacy server-rendered estate adopts Vue: islands with precompiled components, a full-build progressive layer, or a migration path, and who owns template trust.
## The setup that fails A team adds interactivity to pages rendered by a traditional backend template engine. They install `vue`, import it through their bundler, and mount a root component that has **no `template` and no render function**, expecting Vue to take over the existing markup: ```ts import { createApp, ref } from 'vue' createApp({ setup() { const open = ref(false) return { open } } }).mount('#app') ``` In the browser the server-rendered content inside `#app` disappears. In development the console shows: > Component provided template option but runtime compilation is not supported in this build of Vue. Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js". In a production build there is **no warning at all**: just an empty container. ## What actually happens, step by step 1. **Bundler resolution.** `import 'vue'` resolves to `vue.runtime.esm-bundler.js`, the package's default `module` entry. It is the **runtime-only build**: no template compiler is registered. 2. **The mount fallback.** `app.mount()` in `runtime-dom` checks the root component. Because it has neither `render` nor `template`, it copies the container's `innerHTML` into the component's `template` option. This happens in every build. 3. **The container is cleared.** Before mounting, the same code sets the container's `textContent` to an empty string. The server markup is now gone from the page. 4. **Setup finishes without a renderer.** During component setup Vue looks for a render function. The template can only be compiled if a runtime compiler was registered; in the runtime-only build none was, so the instance's render function stays a no-op and nothing is rendered. 5. **The dev warning.** Seeing a `template` but no compiler, the dev build emits the message above, naming the fix for the build it is in: the bundler alias for `esm-bundler`, `vue.esm-browser.js` for the browser ESM build, `vue.global.js` for the global build. ## Telling the two warnings apart | Warning | Meaning | Typical cause | |---|---|---| | *Component provided template option but runtime compilation is not supported in this build of Vue.* | a template exists but cannot be compiled | runtime-only build with a string template, `#id` template or in-DOM root | | *Component is missing template or render function* | there is nothing to compile at all | a component object with neither option, or an empty mount container | Reading the exact text tells you whether to change the **build** or the **component**. ## Ways to fix it - **Use a build with the compiler.** In a bundler, point `vue` at `vue.esm-bundler.js`; without a bundler, load `vue.esm-browser.js` or `vue.global.js`. The in-DOM markup then compiles as the root template. - **Move the markup into Vue.** Turn the enhanced region into an SFC or a render function, so the build compiles it and the runtime-only build is enough. The server keeps rendering the surrounding page. - **Shrink the root.** Mount several small apps on the interactive islands rather than one app on a large region, which limits how much server HTML Vue has to compile and own. ## What does not fix it - Switching to `createSSRApp`: it **hydrates** markup that Vue's own server renderer produced, and it does not copy the container into a template, so a template-less root still has nothing to render. - Adding `v-cloak`: it only hides uncompiled markup until mount; it cannot compile anything. - Setting `app.config.compilerOptions`: the runtime-only build ignores it and warns that compiler options belong in the build setup. ## Confirming the diagnosis Before changing anything, prove which build is loaded and why the root has nothing to render: 1. Read the warning text in a development build: the *provided template option* wording means a compiler is missing, not that the template is wrong. 2. Check what the bundler resolved `vue` to; the default `module` entry is the runtime-only file. 3. Temporarily give the root a trivial `template` string: if it still renders nothing, the build lacks a compiler; if it renders, the problem was the missing template. 4. Confirm the markup really is in the container at mount time, since an empty container produces the *missing template or render function* warning instead. ## Two risks to raise in the interview - **Trust.** Once the full build compiles the container, every `{{ }}` and directive in the server HTML is executed as Vue template code. The Vue security guide says never to mount Vue on nodes that may contain server-rendered, user-provided content: a harmless-looking comment body can become a template expression. - **Parsing.** The markup is browser-parsed before Vue reads it, so in-DOM caveats apply: kebab-case names, explicit closing tags and `is="vue:..."` inside tables.
- When would you see 'Component is missing template or render function' instead?When there is nothing to compile: a component object defines neither `template` nor `render`, or the mount container is empty so the copied template is an empty string. The other warning means a template exists but the build has no compiler, so the exact text tells you whether to fix the component or the build.
- Why is mounting the full build over a whole server-rendered page a security concern?The container's HTML is compiled and its expressions executed as Vue template code. If any of that markup includes user-provided content, text that is harmless as HTML, such as a mustache expression, can become template code. The Vue security guide says never to mount Vue on nodes that may contain server-rendered, user-provided content.
It is like handing a script written in a foreign language to an actor with no translator: the stage is cleared for the performance, but nobody can read the lines, so the stage stays empty.
saying these in an interview costs you the question
- Assumes createApp keeps existing server markup and just attaches listeners
- Switches to createSSRApp to enhance HTML from a non-Vue backend
- Adds v-cloak expecting it to make the markup compile
- Expects the same warning to appear in a production build
- Reads the missing-template warning as meaning the template has a syntax error