What Causes This Error?
This error occurs when you call provide() outside of a component’s setup() function. Like inject(), the provide() function requires an active component instance to register the provided value.
The Problem
import { provide } from 'vue'
// ❌ Called outside setup
provide('theme', { color: 'dark' }) // Error!
export default {
setup() {
// ...
}
}
The Fix
Call provide() Inside setup()
import { provide, reactive } from 'vue'
export default {
setup() {
// ✅ Called inside setup
const theme = reactive({
color: 'dark',
toggle() {
this.color = this.color === 'dark' ? 'light' : 'dark'
}
})
provide('theme', theme)
}
}
With Script Setup
<script setup>
import { provide, reactive } from 'vue'
// ✅ Top-level in script setup works
const theme = reactive({
color: 'dark',
toggle() {
this.color = this.color === 'dark' ? 'light' : 'dark'
}
})
provide('theme', theme)
</script>
Common Scenarios
App-Level Provide
// main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// ✅ App-level provide (different from component provide)
app.provide('globalConfig', {
apiUrl: 'https://api.example.com',
debug: true
})
app.mount('#app')
Creating a Provider Component
<!-- ThemeProvider.vue -->
<script setup>
import { provide, reactive } from 'vue'
const theme = reactive({
color: 'dark',
fontSize: 16,
setColor(color) {
this.color = color
}
})
// ✅ Provide in setup
provide('theme', theme)
</script>
<template>
<slot />
</template>
<!-- App.vue -->
<template>
<ThemeProvider>
<RouterView />
</ThemeProvider>
</template>
Provide with Readonly
<script setup>
import { provide, reactive, readonly } from 'vue'
const state = reactive({
count: 0
})
// ✅ Provide readonly to prevent child mutations
provide('state', readonly(state))
// Provide methods separately for controlled updates
provide('increment', () => state.count++)
</script>
Provide Composable
// useProvideTheme.js
import { provide, reactive, inject } from 'vue'
const ThemeSymbol = Symbol('theme')
export function provideTheme() {
// Must be called in setup()
const theme = reactive({
mode: 'light',
toggle() {
this.mode = this.mode === 'light' ? 'dark' : 'light'
}
})
provide(ThemeSymbol, theme)
return theme
}
export function useTheme() {
// Must be called in setup()
const theme = inject(ThemeSymbol)
if (!theme) {
throw new Error('useTheme requires provideTheme in ancestor')
}
return theme
}
<!-- Root component -->
<script setup>
import { provideTheme } from './useProvideTheme'
provideTheme() // ✅ Called in setup
</script>
<!-- Child component -->
<script setup>
import { useTheme } from './useProvideTheme'
const theme = useTheme() // ✅ Called in setup
</script>
Using Symbols for Keys
// keys.js
export const ThemeKey = Symbol('theme')
export const UserKey = Symbol('user')
export const ApiKey = Symbol('api')
// Provider component
import { ThemeKey, UserKey } from './keys'
provide(ThemeKey, theme)
provide(UserKey, user)
// Consumer component
const theme = inject(ThemeKey)
const user = inject(UserKey)
Quick Checklist
- Call
provide()insidesetup()or<script setup> - Use
app.provide()for app-level globals - Consider using Symbols as keys to avoid collisions
- Use
readonly()to prevent accidental mutations - Create provider components for complex state
- Document that composables with
provide()must be called in setup