Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Sunshine And March Vibes (2025 Wallpapers Edition)

      May 24, 2025

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      May 24, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      May 24, 2025

      How To Prevent WordPress SQL Injection Attacks

      May 24, 2025

      Looking for an AI-powered website builder? Here’s your best option in 2025

      May 24, 2025

      SteamOS is officially not just for Steam Deck anymore — now ready for Lenovo Legion Go S and sort of ready for the ROG Ally

      May 23, 2025

      Microsoft’s latest AI model can accurately forecast the weather: “It doesn’t know the laws of physics, so it could make up something completely crazy”

      May 23, 2025

      OpenAI scientists wanted “a doomsday bunker” before AGI surpasses human intelligence and threatens humanity

      May 23, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      A timeline of JavaScript’s history

      May 23, 2025
      Recent

      A timeline of JavaScript’s history

      May 23, 2025

      Loading JSON Data into Snowflake From Local Directory

      May 23, 2025

      Streamline Conditional Logic with Laravel’s Fluent Conditionable Trait

      May 23, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Open-Typer is a typing tutor application

      May 24, 2025
      Recent

      Open-Typer is a typing tutor application

      May 24, 2025

      RefreshOS is a distribution built on the robust foundation of Debian

      May 24, 2025

      Cosmicding is a client to manage your linkding bookmarks

      May 24, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»Using provide/inject in Vue.js 3 with the Composition API

    Using provide/inject in Vue.js 3 with the Composition API

    August 29, 2024

    Editor’s note: This article was last updated by David Omotayo on August 26, 2024 to include updates and new information.

    Usually, when we want to pass data from a parent to a child component, we use props. Vue.js has made this straightforward and easy to do. But we’ve probably experienced frustration at some point when we need to pass data from a parent-level component to a deeply nested child component.

    If we were to use props, we would end up passing the data one level up or down the component tree regardless of how deep the component is in the component tree hierarchy. This is called prop drilling and could cause our app to look more complex than it is. And if it were an application with a simple state, using Vuex in it would be overkill.

    Luckily for us, Vue has the provide/inject API, and with the introduction of the Composition API in Vue 3, it has never been better.

    Using the provide and inject pair, parent components can send data to children components regardless of how deep the component hierarchy is. The parent component has a provide function to supply data, and the child component has an inject function to start using this data.

    In the image above, we have three levels of children components. The data we want to pass is contained in the parent component, and the desired destination for the data is deeply nested in the third level of the component tree. We could achieve this using props, but at the expense of our code’s simplicity and readability. Let’s see how we can do this without sacrificing either.

    First, we need to scaffold a new Vue app using create-vue:

    npm create vue@latest

    Using the provide API

    The provide API is a function used to define the data or objects to be passed down to a child component.

    To use the provide function, we start by explicitly importing it from vue within the script block. Then, within the component’s setup() function, we invoke provide to define the data or objects we want to pass down to child components.

    But before you do that, you need to know that the provide function accepts two parameters:

    Injection key: This is the identifier used to retrieve the value in descendant components. It is usually a string or a symbol
    Value: This is the data associated with the injection key that will be available to the descendant component

    <!– src/components/MyMarker.vue –>
    <script>
    import { inject } from ‘vue’

    export default {
    setup() {
    const userLocation = inject(‘location’, ‘The Universe’)
    const userGeolocation = inject(‘geolocation’)

    return {
    userLocation,
    userGeolocation
    }
    }
    }
    </script>

    After importing the provide function in the code above, we invoke it inside the setup function. Next, we pass the parameters for the first provide function as follows: the injection key – ‘location’ and a single value – ‘North Pole’.

    For the second provide function, we pass an object containing the latitude and longitude values and set its key as ‘geolocation’.

    Using the inject API

    In contrast, the inject API is used to retrieve data that has been made available by an ancestor component, such as the provider component from the previous example, using the provide function.

    As we did with the provide function, we also have to import the inject function from vue. This lets us call and use the function anywhere in our component.

    The inject function also takes two parameters:

    Key — This is the key used to look up the value provided by an ancestor component. The key must match the injection key used in the provide function
    Default value (optional) — This is the fallback value that would be returned if no value is found for the provided key

    Let’s have a look at the code below:

    <!– src/components/MyMarker.vue –>
    <script>
    import { inject } from ‘vue’

    export default {
    setup() {
    const userLocation = inject(‘location’, ‘The Universe’)
    const userGeolocation = inject(‘geolocation’)

    return {
    userLocation,
    userGeolocation
    }
    }
    }
    </script>

    First we import the inject function into our MyMarker component. Then, inside our setup function, we assign the first provide function with a property name ‘location’ to the userLocation variable. We also provide an optional default fallback value, ‘The Universe’.

    Next, we assign the second provide function with a property name of ‘geolocation’ to the userGeoLocation variable. We return both the userLocation and userGeoLocation variables, after which we are free to use their values anywhere in the MyMarker component.

    Making the provide/inject pair reactive

    Sadly, straight out the box, the provide/inject pair is not reactive. Thankfully, there is a way to go about making this happen by using either the ref or reactive function provided by the Vue API.

    We first have to import them from vue, then we invoke the ref or reactive function. We’ll set its parameters to be the value(s) we want to pass to the desired child component and store the function in a variable. We then invoke the provide function and pass the injection key and its value.

    Now, if anything changes in either property, the MyMarker component will automatically be updated as well!

    We can now update our code as follows:

    <!– src/components/MyMap.vue –>
    <template>
    <MyMarker />
    </template>

    <script>
    import { provide, reactive, ref } from ‘vue’
    import MyMarker from ‘./MyMarker.vue’

    export default {
    components: {
    MyMarker
    },
    setup() {
    const location = ref(‘North Pole’)
    const geolocation = reactive({
    longitude: 90,
    latitude: 135
    })

    provide(‘location’, location)
    provide(‘geolocation’, geolocation)
    }
    }
    </script>

    After importing the ref and reactive functions, we invoke the ref function and give it a parameter (the value ‘North Pole’) and then assign the ref function to the location variable.

    For the reactive function, we invoke it and pass it as a parameter in the form of an object. Then we assign the reactive function to the geolocation variable. After we’ve done this, we can call the provide function and pass it the property name and the value of the data we want to pass down.

    In the first provide function, we set the injection key to ‘location’ and set its value equal to location, which is the value we assigned to the ref function.

    While in the second provide function, we set its property name to ‘geolocation’ and its value equal to geolocation, which is the value we assigned to the reactive function.

    Advanced use cases

    Beyond simple data sharing, provide/inject can be used in several advanced scenarios to solve complex component interactions. Here are some advanced use cases:

    Dependency injection patterns

    These are techniques used in managing dependencies in vue applications using the provide/inject function pairs while avoiding pain points such as tightly coupled components. One such pattern is dependency injection for services.

    Dependency injection for services is a technique of sharing services or utilities across multiple components without tightly coupling them to the service’s implementation. For instance, suppose we have a logging service that we want to inject into multiple components:

    // Logger service
    class Logger {
    log(message) {
    console.log(`[LOG]: ${message}`);
    }
    }

    With provide/inject, we can decouple the components from the logger service implementation while retaining the reusability of the components:

    // Provide the logger service
    provide(‘logger’, new Logger());

    // Inject the service
    const logger = inject(‘logger’);
    logger.log(‘This is a log message’);

    Dynamic Injection with Symbols

    When working in a large project, creating multiple provide and inject functions is inevitable, which increases the risk of key collision due to accidental overrides and conflicts when naming keys. However, this can be avoided with the use of symbols.

    With symbols, you can create unique identifiers that make keys more explicit about the intended purpose of the provided value and reduce the risk of name collision.

    To use symbols, first, create a symbol and provide a value in the provider component (parent):

    import { Symbol } from ‘vue’

    const themes = Symbol(‘theme’);

    provide(themes, {
    color: ‘blue’,
    fontSize: ’14px’
    });

    Then, inject the value in the injector component (child):

    const theme = inject(themes);
    console.log(theme) // outputs “theme”

    As you can tell from the example above, symbols require declaring extra variables, which can easily lead to component bloat and maintenance issues. Luckily, we can create a separate utility javascript file that contains and exports all the keys needed throughout the application and dynamically use them in provider and injector components.

    // keys.js
    export const userFirstName = Symbol();
    export const userLastName = Symbol();
    export const userFullName = Symbol();
    export const userAge = Symbol();
    export const userTitle = Symbol();
    export const userDescription = Symbol();

    These keys can then be dynamically imported and used within the provider and injector components:

    // provider component
    import { userFirstName } from ‘./keys.js’

    provide(userFirstName, ‘Luca’);

    // injector component
    import { userFirstName } from ‘./keys.js’;

    const firstName = inject(userFirstName);
    console.log(firstName); // outputs ‘Luca’

    Plugin-like architecture

    Given the provide/inject mechanism’s loosely coupled nature, we can use the function pairs to create a plugin-like system where components can register themselves to a parent or global context. A good example would be a notification system where components can register to a global notification center:

    // provider component
    provide(‘notify’, (message) => {
    notificationCenter.add(message);
    });

    Then, inject a notify function that sends a message to the notification center similar to how plugins work:

    // injector component
    const notify = inject(‘notify’);
    notify(‘New message received’);

    Simplifying tests with provide/inject

    Using provide and inject can significantly simplify testing in Vue applications, especially when you need to mock certain dependencies. Injecting mocked dependencies, state, or services can let you isolate components under test and control the behavior of its dependencies.

    For example, if we have want to test our previous Logger service example in isolation:

    // LoggerService.js
    export class LoggerService {
    log(message) {
    console.log(message);
    }
    }

    // MyComponent.vue
    <template>
    <div>{{ message }}</div>
    </template>

    <script>
    export default {
    setup() {
    const logger = inject(‘logger’);
    const message = ‘Hello, World!’;
    logger.log(message);
    return { message };
    }
    };
    </script>

    We can inject a mock version of the LoggerService class instead of the real one, like so:

    import { mount } from ‘@vue/test-utils’;
    import MyComponent from ‘@/components/MyComponent.vue’;

    test(‘logs the message on creation’, () => {
    const mockLogger = {
    log: jest.fn(), // Mock the log function
    };

    const wrapper = mount(MyComponent, {
    global: {
    provide: {
    logger: mockLogger, // Inject the mock logger
    },
    },
    });

    expect(mockLogger.log).toHaveBeenCalledWith(‘Hello, World!’);
    });

    With this approach, you can focus on testing the component’s behavior without worrying about the implementation details of the LoggerService class.

    Controlling test environment

    Another way to simplify testing is to create a controlled and predictable environment where components can be isolated and focus on its behavior without interfering with external factors.

    A good example for this is determining whether a user is logged in, and in components, that depends on an authentication service:

    // AuthService
    export class AuthService {
    isAuthenticated() {
    return true; // Actual implementation
    }
    }

    // MyComponent
    <template>
    <div v-if=”isLoggedIn”>Welcome back!</div>
    <div v-else>Please log in.</div>
    </template>

    <script>
    export default {
    setup() {
    const auth = inject(‘auth’);
    const isLoggedIn = auth.isAuthenticated();
    return { isLoggedIn };
    }
    };
    </script>

    We can easily control the authentication state by injecting a mock AuthService:

    import { mount } from ‘@vue/test-utils’;
    import MyComponent from ‘@/components/MyComponent.vue’;

    test(‘shows login prompt when user is not authenticated’, () => {
    const mockAuth = {
    isAuthenticated: jest.fn().mockReturnValue(false), // Mock the auth service to simulate unauthenticated state
    };

    const wrapper = mount(MyComponent, {
    global: {
    provide: {
    auth: mockAuth, // Inject the mock auth service
    },
    },
    });

    expect(wrapper.text()).toContain(‘Please log in.’);
    });

    test(‘welcomes the user when authenticated’, () => {
    const mockAuth = {
    isAuthenticated: jest.fn().mockReturnValue(true),
    };

    const wrapper = mount(MyComponent, {
    global: {
    provide: {
    auth: mockAuth,
    },
    },
    });

    expect(wrapper.text()).toContain(‘Welcome back!’);
    });

    This makes it simple to test different scenarios, in this case Authenticated and Unauthenticated, without changing the actual component code.

    Pain points and how to avoid them

    Like every other tool out there, the provide/inject function pair is not without its shortcomings. There are several pain points and potential drawbacks you can encounter while working with them.

    Implicit dependency

    The provide/inject mechanism can create hidden or hard-to-track dependencies which makes it harder to track which component relies on which data. This can lead to issues where it is unclear whether certain data is available or missing in a component, making the codebase harder to maintain, especially in large projects.

    Solution:

    Document the provide/inject relationships in your components and make sure to use clear and consistent naming conventions for provided keys to make it obvious what is being injected.

    Limited debugging and tooling support

    Debugging issues related to provide/inject can be challenging because the data flow is less linear than with props. Unlike props, injected values are not easily inspectable in development tools like Vue’s dev tool. This can lead to longer debugging sessions when trying to track down which data isn’t available in a particular component.

    Solution:

    There isn’t a written rule on how to debug provide/inject related issues. How you go about this will depend entirely on how frequently you use the function pairs. One way to avoid or lessen the need to debug is to temporarily log the injected values to ensure they are what’s expected.

    Tightly coupled components

    The provide/inject mechanism aimed to fix the issue of component coupling, which is a situation where components are heavily dependent on each other in a way that makes them difficult to modify or reuse. Ironically, the function pair can introduce a new form of coupling when components become heavily reliant on injected values.

    This can make it challenging to reuse such components in different contexts where those values are not provided, ultimately reducing the reusability of components and making refactoring challenging.

    Solution:

    Avoid injecting highly specific data that makes the child components less reusable, and make sure to inject abstract services or interfaces instead of base implementations.

    Unclear data flow

    The data flow with provide/inject is less explicit compared to props. When data is passed through props, it is clear in the component’s interface what it expects and where its coming from. With provide/inject, this relationship is hidden, making the component’s architecture harder to reason about and to trace data origins.

    Solution:

    Use descriptive and self-explanatory keys for injected values to make what they represent is clear. Additionally, instead of injecting individual values, consider injecting a context object that groups related dependencies together.

    When to use the provide inject function pair

    The primary determinant for using the provide/inject mechanism is the simplicity or complexity of your application. The provide and inject function pair is the gray area between using props for small applications and adopting a state management tool for large, complex ones.

    Here is a list of indicators for when to use the provide and inject function pairs:

    If the app has a fairly simple state and using Vuex would be overkill
    If your application has too many component levels, and the components in between don’t use the data before it’s passed to the desired component
    If the data is only used by a few components. But if the data will be used by many more components, Vuex would be a better solution

    Conclusion

    We’ve learned how to use the provide inject function pair to pass data between deeply nested components in Vue.js 3 with the Composition API. We’ve covered how to make it reactive and also the different use cases for when you should use it. To learn more about the provide inject function pair, visit the official documentation.

    The post Using <code>provide</code>/<code>inject</code> in Vue.js 3 with the Composition API appeared first on LogRocket Blog.

    Source: Read More

    Facebook Twitter Reddit Email Copy Link
    Previous ArticlePWA phishing on Android and iOS – Week in security with Tony Anscombe
    Next Article Survey reveals the usage and perception of AI in the overall workforce, beyond just the realm of software development

    Related Posts

    Security

    Nmap 7.96 Launches with Lightning-Fast DNS and 612 Scripts

    May 25, 2025
    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-47568 – ZoomSounds Deserialization Object Injection Vulnerability

    May 25, 2025
    Leave A Reply Cancel Reply

    Continue Reading

    Cross-Platform JavaScript Stealer Targets Crypto Wallets in New Lazarus Group Campaign

    Development

    Adobe ColdFusion Vulnerability: Critical Bug (CVE-2024-53961) with PoC Exploit Code Discovered

    Development

    Fighting osteoporosis before it starts

    Artificial Intelligence

    Preparing for Salesforce Spring ’25 Release Updates: Miscellaneous Updates

    Development
    Hostinger

    Highlights

    CSS Auto Transition: A Game Changer for Developers

    July 1, 2024

    The news: transitioning to auto is now a thing! Well, it’s going to be a thing. Chrome Canary recently…

    Understanding Variables, Data Types, and Constants in VBA

    January 9, 2025

    Design is not what we make. Design is what we make possible.

    February 7, 2025

    CVE-2025-4374 – Quay Unauthorized Privilege Escalation Vulnerability

    May 6, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.