skip to content

Application Instance

createApp builds the application instance you mount, configure through app.config and extend with plugins, error handlers and custom elements. Interviewers probe app-level setup order.

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

explore

questions

23

In Vue 3, how do you make a $formatCurrency helper available in every component template, and what Vue 2 mechanism did that replace?

level: juniorimportance: must knowfreq 55%

answer

  1. a field on app.config
  2. replaces the Vue 2 prototype
  3. templates and this
  4. own properties win
  5. not in script setup code

basics

~20 s

Assign it to app.config.globalProperties.$formatCurrency before mounting. It replaces Vue 2's Vue.prototype: templates and Options API this can read it, but a component's own property of the same name wins and script setup code cannot use this.

solid answer

~30 s

In `main.ts`, set `app.config.globalProperties.$formatCurrency = (n: number) => formatter.format(n)` before `app.mount()`. Every component in that app can then write `{{ $formatCurrency(total) }}` in its template, and Options API code can call `this.$formatCurrency`. This is the Vue 3 replacement for Vue 2's `Vue.prototype`, scoped to one app instead of the whole page. Two limits matter: a component's own property with the same name, such as an Options API method, shadows it, and `<script setup>` code has no `this`, so logic there should import the function directly. Because globals are invisible dependencies, use them sparingly.

code

vue · 12 lines
vue
<script setup lang="ts">
import { formatCurrency } from './format' // script logic imports it

const props = defineProps<{ total: number }>()
const summary = `Total: ${formatCurrency(props.total)}`
</script>

<template>
  <!-- the template can use the global property -->
  <p>{{ $formatCurrency(total) }}</p>
  <p>{{ summary }}</p>
</template>

go deeper

for a junior

Recall that app.config.globalProperties replaced Vue.prototype and that you set it in main.ts before mounting.

for a middle

Explain the proxy lookup order, why own properties shadow globals, and why script setup code cannot reach them through this.

for a senior

Judge when a global helper is worth its hidden dependency, and how it affects tests, typing and name collisions across libraries.

for a principal

Set a policy: a short list of template-only globals, everything stateful injected or imported, so dependencies stay visible across teams.

## What app.config.globalProperties is Every Vue 3 application instance, created with `createApp(App)`, exposes a `config` object. One of its fields, `globalProperties`, is a plain object whose keys become readable on **every component instance** in that app. It is the official replacement for Vue 2's `Vue.prototype`, which no longer exists in Vue 3. ```ts import { createApp } from 'vue' import App from './App.vue' const formatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }) const app = createApp(App) app.config.globalProperties.$formatCurrency = (n: number) => formatter.format(n) app.mount('#app') ``` Any template in the app can now write `{{ $formatCurrency(order.total) }}`. ## How lookup works When a template expression or Options API `this` reads a name, Vue's component proxy searches in a fixed order: 1. the component's own state: setup bindings, `data`, props and other context properties; 2. Vue's built-in public properties such as `$el`, `$props` or `$attrs`; 3. finally, `app.config.globalProperties`. So a global property is a **fallback**. If a component defines its own `$formatCurrency`, that one wins silently, and a global property cannot replace a built-in `$` property. ## Vue 2 versus Vue 3 | | Vue 2 `Vue.prototype.$x` | Vue 3 `app.config.globalProperties.$x` | |---|---|---| | Scope | every instance on the page | only components of that one app | | Mechanism | the JavaScript prototype chain | a lookup fallback in the component proxy | | Functions | reached through the prototype, called as methods | returned as-is, not bound to the component | | Script setup | not applicable | not reachable through `this` | The third row has a practical consequence: in Vue 3 a function stored in `globalProperties` should not rely on `this` being the calling component. Write it as a closure over the values it needs. ## Where it does not reach - **`<script setup>` logic.** The block compiles to `setup()`, which has no component `this`. The template of that same component still reaches the global, but script code should simply `import { formatCurrency } from './format'`. - **Composables and stores.** They do not go through the component proxy, so they cannot read globals by name either. - **Other apps on the page.** Each app has its own `config`. ## Rules around app.config - **Mutate fields, never replace the object.** `app.config = { ... }` is ignored, and development builds warn *app.config cannot be replaced. Modify individual options instead.* - **Configure before `mount()`.** Global properties are read at access time, but anything the first render needs should be in place before it runs. - **Type it.** TypeScript cannot see a runtime assignment, so a global property also needs a type declaration; that augmentation step is covered with plugins. ## Global property or provide? Both `globalProperties` and `app.provide()` share a value with the whole app, so interviewers often ask when to pick which: | Need | Better fit | |---|---| | a stateless helper called from many templates | `globalProperties` | | a service with state or configuration | `app.provide()` plus `inject()` | | typed access from `<script setup>` and composables | `app.provide()` with an `InjectionKey` | | Options API code that already uses `this.$x` everywhere | `globalProperties` | A `$formatCurrency` formatter sits at the boundary: it is stateless and template-heavy, so a global property is reasonable, but the same function should also be importable so script code and tests can use it without the app. ## When not to use it Global properties are **implicit dependencies**: a reader of a component cannot tell where `$formatCurrency` comes from, tests must remember to install it, and every library shares the same `$` namespace. They fit a small number of template helpers that are used everywhere. For anything with state or configuration, an imported function or an injected service is easier to trace, type and test. ## Common mistakes - Expecting `this.$formatCurrency` to work inside `<script setup>`. - Relying on `this` inside the global function. - Adding a global with the same name as a component's own property and wondering which one runs. - Assuming a global set on one app is visible in a second app on the same page. - Returning a `$`-prefixed name from `data()` or `setup()` to override a global: Vue reserves the `$` and `_` prefixes and does not expose such keys on the instance (development warns), so the realistic collision is an Options API method of the same name.

  • Why does this inside a Vue 3 global property function not point at the component?
    When a template reads a global property, Vue's proxy returns the stored value as-is; it does not bind functions to the calling instance. The template calls it as a plain function, so `this` is not the component. Write global helpers as closures or pure functions, and pass anything component-specific as an argument.
  • How would a component test provide $formatCurrency?
    The test creates its own app, so it must add the global itself, for example through the test utility's global mounting options, or it must install the same setup the real app uses. Relying on `main.ts` does nothing in a test, which is one of the costs of global properties.

saying these in an interview costs you the question

  • Vue 3 still supports Vue.prototype for adding instance properties
  • A global property overrides a component's own property of the same name
  • Global properties set on one app are visible to every app on the page
  • script setup code can call this.$formatCurrency like Options API code
  • Replacing app.config with a new object applies the new settings
open as a page

In Vue 3, what does app.mount() return, and why does createApp(App).mount('#app').use(plugin) fail?

level: juniorimportance: must knowfreq 70%

basics

~20 s

app.mount() renders the root component into the container and returns the root component instance, not the app. That instance has no use() method, so chaining .use() after mount throws; configure and register everything first, then call mount last.

open as a page

In Vue 3, what is a plugin, and what exactly happens when you call app.use(plugin, options)?

level: juniorimportance: must knowfreq 62%

basics

~20 s

A Vue 3 plugin is an object with an install(app, options) method, or a bare function used as install. app.use calls it synchronously with the app and any extra arguments, installs it once per app, and returns the app.

open as a page

In Vue 3, what does `app.config.errorHandler` receive, which errors reach it, and what does Vue do with an error when no handler is set?

level: juniorimportance: should knowfreq 42%

basics

~20 s

Vue 3's app.config.errorHandler receives (err, instance, info) for errors in renders, setup, hooks, watchers, event handlers and directive or transition hooks that no onErrorCaptured stopped. Without it, Vue rethrows in development and only logs in production.

open as a page

In Vue 3, why does a native custom element like `<color-swatch>` trigger "Failed to resolve component", and how does `compilerOptions.isCustomElement` fix it?

level: juniorimportance: should knowfreq 32%

basics

~20 s

Vue 3's compiler treats any non-native tag as a component and warns when none is registered. compilerOptions.isCustomElement, a tag-name predicate, makes it emit a plain element instead; with a build step it is set in the SFC build plugin.

open as a page

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%

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.

open as a page

In Vue 3, what does app.config.warnHandler receive, how does it differ from app.config.errorHandler, and why is it not a production hook?

level: middleimportance: should knowfreq 45%

basics

~10 s

warnHandler(msg, instance, trace) intercepts Vue's development warnings, which do not exist in production builds. errorHandler(err, instance, info) receives uncaught runtime errors in both development and production, so only errorHandler belongs in production reporting.

open as a page

In Vue 3, what happens to the existing HTML inside the mount container when app.mount() runs?

level: middleimportance: should knowfreq 35%

basics

~20 s

The container element stays and its content is replaced by the rendered app. If the root component has no template or render function, Vue first uses the container's innerHTML as the root template, which needs the full build.

open as a page

In Vue 3, how do you pass initial data to the root component through createApp's second argument, and what are the limits of root props?

level: middleimportance: should knowfreq 40%

basics

~20 s

Pass an object as createApp(App, { userId: 42 }) and declare it in the root with defineProps. The object must be an object, and its top-level keys are read once at mount, so later mutations to it do not update the root.

open as a page

In Vue 3, what does app.unmount() do, and can the same app instance be mounted again afterwards?

level: middleimportance: should knowfreq 28%

basics

~20 s

app.unmount() tears down the whole component tree, running every component's unmount hooks and clearing the container. The same app instance cannot be mounted again; create a fresh app, usually from a factory function, for each mount.

open as a page

In Vue 3, which errors can `onErrorCaptured` and `app.config.errorHandler` see, and which never reach them, like a throw inside a `setTimeout` callback?

level: middleimportance: should knowfreq 38%

basics

~20 s

Vue 3 routes only errors from code it calls itself: renders, setup, lifecycle hooks, watchers, v-on handlers, directive and transition hooks, plus promises those return. Timer callbacks, un-returned promises and addEventListener listeners never reach its handlers.

open as a page

In Vue 3, in what order do `onErrorCaptured` hooks and `app.config.errorHandler` run for a descendant's error, and what does returning `false` change?

level: middleimportance: should knowfreq 45%

basics

~20 s

In Vue 3 an error starts at the failing component's parent and climbs the parent chain, running every onErrorCaptured hook bottom to top, then app.config.errorHandler. A hook returning false marks it handled: later hooks and the app handler are skipped.

open as a page

How would you write a small Vue 3 i18n plugin that offers a $t helper in templates and a provided API for script setup code?

level: middleimportance: should knowfreq 45%

basics

~20 s

Export an object whose install(app, options) keeps the locale in reactive state, assigns a $t function to app.config.globalProperties for templates, and calls app.provide with an InjectionKey so script setup code can inject the same typed API.

open as a page

In a Vue 3 TypeScript project, how do you type a $t property that a plugin adds through app.config.globalProperties?

level: middleimportance: should knowfreq 40%

basics

~20 s

Augment Vue's ComponentCustomProperties interface with declare module 'vue' in a file that is itself a module (has a top-level import or export), and make sure tsconfig includes it; templates and this then know $t's type.

open as a page

When a Vue 3 component is wrapped with `defineCustomElement`, how do its props and emitted events appear to a page that does not use Vue?

level: middleimportance: should knowfreq 30%

basics

~20 s

Vue 3's defineCustomElement returns an HTMLElement subclass to register with customElements.define. Declared props become element properties synced with attributes, cast for Number and Boolean types; each emit dispatches a CustomEvent whose detail is the array of arguments.

open as a page

A server-rendered legacy page needs two independent Vue 3 widgets; how would you mount them, and what is isolated versus shared between the two apps?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Create one app per widget with createApp and mount each on its own element, not one app over the whole page. Configuration, registrations, plugins and provides are per app; imported modules, including any module-level state, are shared.

open as a page

A dashboard widget throws during render in Vue 3; how do you show a fallback for just that widget with `onErrorCaptured` while the rest of the page keeps working?

level: seniorimportance: should knowfreq 36%

basics

~20 s

In Vue 3, wrap each widget in a component whose onErrorCaptured stores the error in a ref and whose template then shows a fallback instead of the slot. Report the error, never re-render the failing content, and retry with a new key.

open as a page

A Vue 3 plugin keeps its state in module-level variables and starts a timer in install; what goes wrong when several apps use it, and how do you fix it?

level: seniorimportance: should knowfreq 25%

basics

~20 s

Module state is shared by every app that installs the plugin, while install, and its timer, runs once per app and never stops. Create state per app inside install or a factory, and clear the timer in app.onUnmount (Vue 3.5+).

open as a page

A Vue 3 widget built with `defineCustomElement` renders in a shadow root, but its nested child components appear unstyled; why, and how do you fix it?

level: seniorimportance: should knowfreq 22%

basics

~20 s

Ordinary Vue SFC styles are extracted into the page's CSS file, which cannot reach inside a shadow root. Only SFCs compiled in custom element mode, such as .ce.vue files, carry CSS for injection, so nested children need that mode too.

open as a page

In Vue 3.5, why would you set app.config.idPrefix when two separate Vue apps render on the same page?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

useId() numbers ids per app with the default prefix v, so two apps both generate v-0, v-1 and so on. Duplicate DOM ids break label and ARIA links; giving each app its own app.config.idPrefix keeps the ids unique.

open as a page

In a Vue 3 template, how does `:items="list"` on a native custom element pick property or attribute, and when do `.prop` or `.attr` help?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

Vue 3 checks the key with the in operator: if the element has that property, Vue sets it, otherwise it sets an attribute, stringifying objects. The .prop modifier forces a property; .attr forces an attribute.

open as a page

In Vue 3.5, what does app.config.throwUnhandledErrorInProduction change, and when would you turn it on?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

Vue rethrows unhandled errors in development but only logs them in production. Setting throwUnhandledErrorInProduction to true makes production rethrow too, so page-level error listeners and server code see them; it has no effect once errorHandler is set.

open as a page

When is shipping Vue 3 components as custom elements via `defineCustomElement` the wrong choice, and which Vue features stop working across the element boundary?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

Vue 3 custom elements carry the Vue runtime, roughly 16 kB, so one small widget fits plain JavaScript better and Vue hosts should use normal components. At the boundary, scoped slots, v-slot and injection from ordinary components are lost.

open as a page