skip to content

Why does setting app.config.compilerOptions.delimiters have no effect in a Vue 3 app whose templates are single-file components built with Vite?

level: middleimportance: should knowfreq 25%

answer

  1. when are templates compiled?
  2. runtime-only build
  3. no compiler in the browser
  4. dev warning explains
  5. pass them to the SFC build plugin

basics

~20 s

app.config.compilerOptions configures only the in-browser template compiler of Vue's full build. SFC templates are compiled at build time and the app ships the runtime-only build, so the options must go to the SFC build plugin's compiler options instead.

solid answer

~30 s

`app.config.compilerOptions` (with `isCustomElement`, `whitespace`, `delimiters` and `comments`) is handed to Vue's **runtime** template compiler, the one used for in-DOM templates and `template` strings. In a Vite project with `.vue` files, templates are compiled to render functions during the build, and the bundle uses the runtime-only build, which contains no compiler, so there is nothing at runtime for these options to affect. In development Vue warns that `compilerOptions` is only respected with the full build, and points you to the SFC plugin's options. The fix is to pass the same options to the build-time template compiler through that plugin's configuration.

go deeper

for a junior

Recall that SFC templates are compiled during the build, so runtime config cannot change how they were compiled.

for a middle

Explain the full versus runtime-only build split, the four compiler options with their defaults, and where to pass them in an SFC project.

for a senior

Diagnose a no-op compiler setting from the development warning, and keep compiler configuration in the build rather than shipping the compiler.

for a principal

Choose between build-time and runtime compilation for a product: runtime templates cost bundle size and trust in template content.

## Two places a Vue template can be compiled A Vue template is not executed as HTML; it is **compiled** into a render function. That compilation can happen in two places: - **At build time.** A `.vue` single-file component is processed by the bundler's Vue plugin, which compiles `<template>` into JavaScript. The browser only receives render functions, so the app can use Vue's smaller **runtime-only** build, which has no template compiler. - **At runtime.** A root component with no template reads the mount container's HTML, or a component defines `template: '<div>...</div>'` as a string. Something must compile that in the browser, which requires the **full build** that includes the compiler. `app.config.compilerOptions` configures only the second case. ## What the options are | Option | Type | Default | Effect | |---|---|---|---| | `isCustomElement` | `(tag: string) => boolean` | none | treat matching tags as native custom elements, not Vue components | | `whitespace` | `'condense' \| 'preserve'` | `'condense'` | how whitespace between and inside elements is kept | | `delimiters` | `[string, string]` | the double-brace mustache pair | change the interpolation syntax | | `comments` | `boolean` | `false` | keep HTML comments in production output | Values set on `app.config.compilerOptions` apply to every runtime-compiled template in that app, and a component can override them with its own `compilerOptions` option, which is also a runtime-compiler feature. ## Why the SFC project ignores them 1. The developer writes `app.config.compilerOptions.delimiters = ['${', '}']` in `main.ts`. 2. But every template already became a render function during the build, compiled with the default delimiters. 3. The shipped runtime-only build has no compiler that could read the setting. 4. So the templates still use the double-brace syntax, and text written with `${ }` renders literally. Vue does not leave this silent. In development, when a runtime-only build reads or writes `app.config.compilerOptions`, it warns that the option is only respected when using a build that includes the runtime compiler, and that the options must be passed to `@vue/compiler-dom` in the build setup, naming the Vite Vue plugin's options as the place to do it. ## Where to put them instead For SFCs, pass the same compiler options to the build-time compiler through the bundler's Vue plugin configuration. The option names and meanings are the same; only the place changes. Because the configuration then lives in the build, it applies to every SFC in the project rather than to one app instance. ## When app.config.compilerOptions is right - Vue loaded without a build step, using the full build, with templates written in the page. - A server-rendered page that Vue enhances with an in-DOM root template, where the server's own templating also uses double braces and `delimiters` avoids the clash. - Content that arrives as template strings at runtime, which also requires the full build. ## Recognising it in a code review - The project has `.vue` files and a bundler, yet `main.ts` touches `app.config.compilerOptions`: the setting is almost certainly dead code. - Custom element tags still produce a *Failed to resolve component* warning, or text written with new delimiters renders literally: the runtime setting never reached the compiler that built those templates. - The same compiler option appears both in the bundler configuration and in `main.ts`: only the build-time one affects SFCs, so delete the other to avoid confusion. The inverse also happens: a page that uses in-DOM templates with the full build must configure the runtime option, because the build-time plugin never sees those templates. ## Common mistakes - Setting `compilerOptions` in `main.ts` of an SFC project and assuming it worked because nothing crashed. - Switching the whole project to the full build just to make the config apply, which ships the compiler to every user. - Expecting `comments: true` to matter in development: comments are always kept in development and the option changes production output only.

  • Can a single component override app.config.compilerOptions?
    Yes. A component can declare its own `compilerOptions` option, which overrides the app-level values for that component's template. Like the app-level setting, it only matters when that template is compiled at runtime by the full build; a build-time compiled SFC is unaffected.
  • Why would you change delimiters at all?
    Mainly when Vue enhances a server-rendered page whose server-side template engine also uses double braces. Changing Vue's delimiters, for example to `['${', '}']`, lets both syntaxes coexist in one HTML file without the server consuming Vue's expressions first.

saying these in an interview costs you the question

  • app.config.compilerOptions configures how SFC templates are compiled in a Vite build
  • Runtime-only builds silently ignore compilerOptions with no warning
  • The fix is to always ship Vue's full build
  • comments: true is needed to keep comments during development
  • compilerOptions changes affect templates already compiled at build time