Why does setting app.config.compilerOptions.delimiters have no effect in a Vue 3 app whose templates are single-file components built with Vite?
answer
- when are templates compiled?
- runtime-only build
- no compiler in the browser
- dev warning explains
- pass them to the SFC build plugin
basics
~20 sapp.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
Recall that SFC templates are compiled during the build, so runtime config cannot change how they were compiled.
Explain the full versus runtime-only build split, the four compiler options with their defaults, and where to pass them in an SFC project.
Diagnose a no-op compiler setting from the development warning, and keep compiler configuration in the build rather than shipping the compiler.
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