skip to content

Runtime & Compiler Builds

The vue package ships runtime-only builds and full builds with an in-browser template compiler. Interviewers probe in-DOM template caveats and why most apps precompile templates.

part ofVue.jsoverview, primer and where to startread it →
on this pageshow

explore

questions

4

In Vue 3, how does the runtime-only build differ from the full build, and why do most apps ship the runtime-only one?

level: juniorimportance: should knowfreq 42%

answer

  1. who turns templates into functions
  2. compiler shipped or not
  3. build step precompiles SFC templates
  4. default bundler entry is runtime-only

basics

~20 s

The full build includes Vue's template compiler, so it can compile template strings in the browser; the runtime-only build does not. Apps with a build step precompile templates into render functions, so shipping the compiler would be wasted bytes and work.

solid answer

~40 s

Vue needs a render function for every component. The **full build** (`vue.global.js`, `vue.esm-browser.js`, `vue.esm-bundler.js`) bundles the template compiler and compiles `template` strings, `#id` selectors or the mount container's HTML on the fly. The **runtime-only build** leaves the compiler out and expects render functions to exist already. With a bundler, SFC `<template>` blocks are compiled at build time, so the default `vue` entry is `vue.runtime.esm-bundler.js`: users skip roughly 14 kB of compiler, pay no compile cost at startup, template errors surface at build time, and no `new Function` call collides with a strict Content Security Policy. You reach for the full build only when templates exist as strings at runtime, such as a no-build page.

code

html · 14 lines
html
<div id="app">
  <button @click="count++">Clicked {{ count }} times</button>
</div>
<script type="module">
  // vue.esm-browser.js served as a static file: the full build, no bundler
  import { createApp, ref } from './vendor/vue.esm-browser.js'

  createApp({
    setup() {
      const count = ref(0)
      return { count }
    }
  }).mount('#app')
</script>

go deeper

for a junior

Recall that templates must become render functions, that the full build carries a compiler and the runtime-only build does not, and that SFCs are compiled by the build step.

for a middle

Name the dist files and which is the bundler default, and explain the costs of runtime compilation: payload, startup work, late errors and the new Function call.

for a senior

Diagnose a template that will not render under the runtime-only build, and decide when a no-build page or runtime string templates genuinely justify the full build.

for a principal

Weigh a no-build progressive-enhancement approach against introducing a build step across teams, including CSP policy and where template compile errors should be caught.

## What the two builds contain Every Vue 3 component ultimately needs a **render function**: a function that returns a tree of virtual DOM nodes (vnodes) for the renderer to mount and patch. A template is only a convenient way to *describe* that function. Something has to translate the template into JavaScript, and the `vue` package lets you choose *when* that translation happens. - The **full build** bundles the runtime together with the template compiler (`@vue/compiler-dom`). Its entry registers a compile function with the runtime, so a component that only has a `template` option (a string, a `#selector` pointing at an element, or the mount container's `innerHTML`) is compiled into a render function the first time it is set up. - The **runtime-only build** ships the renderer, the reactivity system and the built-in components, but no compiler. Every component must already have a render function when it reaches the browser. In practice the render functions come from a **build step**: the bundler plugin compiles each Single-File Component's `<template>` block ahead of time, so the browser only ever sees JavaScript. ## The dist files you will meet | File | Compiler included | Typical use | |---|---|---| | `vue.runtime.esm-bundler.js` | no | default entry for bundlers (the `module` field) | | `vue.esm-bundler.js` | yes | bundler setups that still compile strings at runtime | | `vue.global.js` / `vue.runtime.global.js` | yes / no | a plain `<script src>` tag, exposes a `Vue` global | | `vue.esm-browser.js` / `vue.runtime.esm-browser.js` | yes / no | native `<script type="module">` or an import map | | `vue.cjs.js` | yes | Node.js server-side rendering via `require()` | The global and `esm-browser` files come with `.prod.js` variants that are pre-minified with production branches hard-coded; the `esm-bundler` files leave `process.env.NODE_ENV` checks for your bundler to replace. ## How runtime compilation works When the full build meets a component without a render function, it: 1. Reads the template source: the string itself, or the `innerHTML` of the element a `#id` selector points to. 2. Runs the template compiler on it (with the `hoistStatic` option on), producing JavaScript source code. 3. Turns that source into a function with `new Function(...)`, marks it as runtime-compiled and caches it keyed by the template string and options, so a second instance of the same component skips compilation. Runtime-compiled render functions access component state through a `with` block and a dedicated proxy, which is why the compiled output differs slightly from what an SFC build emits. ## Why most apps precompile - **Payload**: the Vue performance guide puts the compiler at roughly 14 kB min+gzip that a precompiled app never downloads. - **Startup cost**: compiling happens on every page load in every user's browser instead of once on a build machine. - **Earlier errors**: a malformed template fails the build instead of logging a compile warning in a user's console. - **Content Security Policy**: because the full build creates functions with `new Function`, a policy that forbids `unsafe-eval` blocks it; precompiled render functions need no such exemption. - **Tooling**: SFCs bring scoped CSS, `<script setup>` compilation and type checking, all of which assume a build step anyway. Runtime-compiled templates are not unoptimized: the in-browser compiler still emits patch flags and caches static nodes. A few optimizations do depend on build-time compilation, such as caching inline event handlers and stringifying large static regions, but the main difference is where and when compilation runs, and what the browser has to download. ## When the full build is the right choice - Progressive enhancement of a page whose markup comes from a non-JavaScript backend, with no build step at all (`vue.global.js` or `vue.esm-browser.js` from a CDN). - A bundled app that must still compile template strings at runtime, for example templates stored in a CMS. Then the bundler's `vue` import is pointed at `vue.esm-bundler.js`. - Options such as `app.config.compilerOptions` (for example `isCustomElement` or `delimiters`) are only honoured by the full build; with the runtime-only build the same options are passed to the build-time compiler instead, and the runtime warns if you set them on the app. ## A quick way to tell which one you are running If a component with a `template` string renders nothing and the console says that runtime compilation is not supported in this build, you are on a runtime-only build. If `.vue` files work but a string template does not, the build step is doing the compiling and the runtime has no compiler: exactly the default setup.

  • Do templates compiled in the browser by the full build lose Vue's compiler optimizations?
    Mostly not. The in-browser compiler still emits patch flags and caches static nodes, and it caches each compiled render function by template string. Two build-only extras are missing: inline event-handler caching and stringification of large static regions. The bigger costs are downloading the compiler, compiling on each page load, and losing build-time error reporting.
  • Why can the full build conflict with a strict Content Security Policy?
    The runtime compiler turns generated source code into a function with `new Function`. A policy that disallows `unsafe-eval` blocks that call, so in-browser compilation fails. Precompiled render functions are ordinary module code and need no eval exemption, which is one more reason production apps precompile.
  • Where do `app.config.compilerOptions` such as `isCustomElement` go in a runtime-only setup?
    They go to the build-time compiler through the bundler's Vue plugin options, because `app.config.compilerOptions` is only honoured by the full build. In development the runtime-only build warns when you read or set it on the app, pointing you at the build configuration instead.

saying these in an interview costs you the question

  • Says the runtime-only build cannot use templates at all, even inside .vue files
  • Claims the full build renders faster because it can compile on the fly
  • Thinks importing 'vue' in a bundler gives the full build by default
  • Believes runtime-compiled templates skip patch flags and static hoisting
  • Calls the in-browser compiler free because it only runs once per template
open as a page

When a Vue 3 template is written directly in a page's HTML, which browser parsing caveats apply and how do you work around each?

level: middleimportance: should knowfreq 32%

basics

~20 s

Vue compiles an in-DOM template only after the browser has parsed it, so names are lowercased, self-closing custom tags stay open and invalid children are hoisted. Use kebab-case, explicit closing tags and is="vue:component-name" on a valid native element.

open as a page

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?

level: seniorimportance: should knowfreq 26%

basics

~20 s

The 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.

open as a page

What are Vue 3's compile-time feature flags such as __VUE_OPTIONS_API__, which builds read them, and what happens if you never define them?

level: middleimportance: nice to knowfreq 18%

basics

~20 s

They are global constants the esm-bundler builds expect the bundler to replace so unused code can be tree-shaken: VUE_OPTIONS_API (default true), VUE_PROD_DEVTOOLS and VUE_PROD_HYDRATION_MISMATCH_DETAILS (both false). Left undefined, Vue applies the defaults and warns in development.

open as a page