Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/provide-inject-synchronous-setup.md
1---2title: Provide Must Be Called Synchronously During Setup3impact: HIGH4impactDescription: Calling provide() asynchronously or conditionally may fail silently or cause inconsistent injection behavior5type: gotcha6tags: [vue3, provide-inject, composition-api, async, setup]7---89# Provide Must Be Called Synchronously During Setup1011**Impact: HIGH** - The `provide()` function must be called synchronously during the component's `setup()` phase. Calling it asynchronously (inside callbacks, promises, or after await) will fail silently, and descendant components will not receive the provided value.1213## Task Checklist1415- [ ] Always call `provide()` at the top level of `setup()` or `<script setup>`16- [ ] Never call `provide()` inside async callbacks or after await statements17- [ ] For async data, provide a ref first, then update its value later18- [ ] Use immediate `provide()` with reactive containers for dynamic data1920## The Gotcha: Async Provide Fails Silently2122**Wrong - Provide after async operation:**23```vue24<script setup>25import { provide } from 'vue'2627// WRONG: provide() called after await - will NOT work28onMounted(async () => {29const userData = await fetchUser()30provide('user', userData) // Silent failure!31})32</script>33```3435**Wrong - Provide inside callback:**36```vue37<script setup>38import { provide } from 'vue'3940// WRONG: provide() inside callback - will NOT work41setTimeout(() => {42provide('config', { theme: 'dark' }) // Silent failure!43}, 0)44</script>45```4647**Wrong - Provide after await in setup:**48```vue49<script setup>50import { provide } from 'vue'5152const response = await fetch('/api/config')53const config = await response.json()5455// WRONG: This is after an await, setup context may be lost56provide('config', config) // May not work reliably57</script>58```5960## Solution: Provide Synchronously, Update Async6162**Correct - Provide ref immediately, update later:**63```vue64<script setup>65import { provide, ref, onMounted } from 'vue'6667// Provide immediately with initial value68const user = ref(null)69const isLoading = ref(true)70const error = ref(null)7172provide('userState', {73user,74isLoading,75error76})7778// Update the ref values asynchronously79onMounted(async () => {80try {81const userData = await fetchUser()82user.value = userData83} catch (e) {84error.value = e85} finally {86isLoading.value = false87}88})89</script>90```9192```vue93<!-- Consumer component -->94<script setup>95import { inject } from 'vue'9697const { user, isLoading, error } = inject('userState')98</script>99100<template>101<div v-if="isLoading">Loading...</div>102<div v-else-if="error">Error: {{ error.message }}</div>103<div v-else>Welcome, {{ user?.name }}</div>104</template>105```106107## Pattern: Async Data Provider108109Create a reusable pattern for async-provided data:110111```vue112<!-- AsyncDataProvider.vue -->113<script setup>114import { provide, ref, onMounted, watch } from 'vue'115116const props = defineProps({117fetchFn: {118type: Function,119required: true120},121provideKey: {122type: [String, Symbol],123required: true124},125immediate: {126type: Boolean,127default: true128}129})130131const data = ref(null)132const isLoading = ref(false)133const error = ref(null)134135async function load() {136isLoading.value = true137error.value = null138139try {140data.value = await props.fetchFn()141} catch (e) {142error.value = e143} finally {144isLoading.value = false145}146}147148// Provide synchronously149provide(props.provideKey, {150data,151isLoading,152error,153reload: load154})155156// Fetch asynchronously157if (props.immediate) {158onMounted(load)159}160</script>161162<template>163<slot />164</template>165```166167Usage:168169```vue170<template>171<AsyncDataProvider172:fetch-fn="() => api.getUser(userId)"173provide-key="userData"174>175<UserProfile />176</AsyncDataProvider>177</template>178```179180## Why This Happens181182Vue's `provide()` relies on the current component instance context, which is only available synchronously during setup. After setup completes:1831841. The setup context is cleared1852. `provide()` can't find the current instance1863. The call fails silently (no error thrown)187188## Checking for Setup Context189190You can verify if setup context is available:191192```js193import { getCurrentInstance } from 'vue'194195function debugProvide(key, value) {196const instance = getCurrentInstance()197198if (!instance) {199console.error(200`provide() called outside setup context. ` +201`Key: ${String(key)}. This will fail silently.`202)203return204}205206provide(key, value)207}208```209210## App-Level Provide (Exception)211212`app.provide()` can be called anytime during app initialization:213214```js215// main.js216import { createApp } from 'vue'217import App from './App.vue'218219const app = createApp(App)220221// This works - app-level provide222app.provide('appConfig', { version: '1.0.0' })223224// Even async is OK at app level before mount225fetchConfig().then(config => {226app.provide('apiConfig', config)227app.mount('#app')228})229```230231But once the app is mounted, `app.provide()` should not be called.232233## Reference234- [Vue.js Composition API - provide()](https://vuejs.org/api/composition-api-dependency-injection.html#provide)235- [Vue.js Provide/Inject Guide](https://vuejs.org/guide/components/provide-inject.html)236