Provide / Inject
Share data across many levels of nested components without passing props through every intermediate layer.
The Prop Drilling Problem
When a deeply nested component needs data from a far-off ancestor, passing it down as a prop through every intermediate component — none of which actually use it — is called prop drilling. It clutters components with props they only forward, never use.
provide()
An ancestor component calls provide() with a key and a value, making that value available to every descendant, no matter how deeply nested.
<!-- App.vue --><script setup>import { provide } from 'vue';
provide('theme', 'dark');</script>inject()
Any descendant component calls inject() with the same key to retrieve that value — regardless of how many layers of components sit in between.
<!-- DeeplyNestedButton.vue --><script setup>import { inject } from 'vue';
const theme = inject('theme', 'light'); // 'light' is the fallback if not provided</script>
<template> <button :class="theme">Click me</button></template>Making Provided Data Reactive
Provide a ref (or reactive object) — not a plain value — if descendants should see updates when the source changes.
<script setup>import { ref, provide } from 'vue';
const theme = ref('dark');provide('theme', theme); // descendants see updates when theme.value changes
function toggleTheme() { theme.value = theme.value === 'dark' ? 'light' : 'dark';}</script>For state genuinely shared across unrelated parts of an app (not just an ancestor-descendant relationship), Pinia — covered in a later lesson — is the standard, more scalable tool. Reserve provide/inject for avoiding prop drilling within one component subtree.
Common Beginner Mistakes
A plain string or number provided directly is a frozen snapshot — provide a ref if descendants need to see later changes.
This makes data flow harder to trace — reach for Pinia when the data isn't naturally scoped to a specific component subtree.
FAQs
It returns undefined, unless you pass a default value as inject()'s second argument — always provide a sensible default for optional dependencies.
Yes — provide/inject follows the component tree, so a closer provide() shadows one further up, exactly like nested scopes.
Summary
provide/inject solves prop drilling for data scoped to a component subtree, without needing a full state management library. With components covered, the next section moves into the Composition API itself in depth.