Vue 3 — Provide and Inject
Tags: webdev, programming, javascript, vue
Published: 2021-01-25 23:39:30

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

**Vue 3 is in beta and it’s subject to change.**

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at providing and injecting data with Vue 3.

### Provide and Inject

To make passing data easier between deeply nested components, we can bypass props and use the `provide` property to expose some data.

And then we can use the `inject` property to get the data.

For example, we can write:

```
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <blog-post></blog-post>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("blog-post", {
        data() {
          return {
            post: { title: "hello world" }
          };
        },
        provide: {
          author: "james"
        },
        template: `
          <div>
            {{ post.title }}
            <blog-post-statistics></blog-post-statistics>
          </div>
        `
      });

      app.component("blog-post-statistics", {
        inject: ["author"],
        template: `
          <p>{{author}}</p>
        `,
        created() {
          console.log(this.author);
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>
```

We have the `blog-post` and `blog-post-statistics` components.

In the `blog-post` component, we have the `provide` property with the `author` property.

This is the data that we’re providing to child components.

Therefore, in the `blog-post-statistics` component, we have the `inject` property with an array of the property names that are in `provide` that we want to get.

So `author` in `blog-post-statistics` would be `author` in the template or `this.author` in component methods.

However, this won’t work with Vue instance properties.

So if we have:

```
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <blog-post></blog-post>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("blog-post", {
        data() {
          return {
            post: { title: "hello world", author: "james" }
          };
        },
        provide: {
          post: this.post
        },
        template: `
          <div>
            {{ post.title }}
            <blog-post-statistics></blog-post-statistics>
          </div>
        `
      });

      app.component("blog-post-statistics", {
        inject: ["post"],
        template: `
          <p>{{post.author}}</p>
        `,
        created() {
          console.log(this.post.author);
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>
```

Then nothing will be displayed.

To fix this, we got to pass in the property that has the value to `provide` and change `provide` to a function:

```
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <blog-post></blog-post>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("blog-post", {
        data() {
          return {
            post: { title: "hello world", author: "james" }
          };
        },
        provide() {
          return {
            author: this.post.author
          };
        },
        template: `
          <div>
            {{ post.title }}
            <blog-post-statistics></blog-post-statistics>
          </div>
        `
      });

      app.component("blog-post-statistics", {
        inject: ["author"],
        template: `
          <p>{{author}}</p>
        `,
        created() {
          console.log(this.author);
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>
```

Now the `provide` is a method of the `blog-post` component instead of a property.

And we return what we want to provide to child components.

### Working with Reactivity

Changes with reactive properties in the parent won’t be reflected in child components when we update them.

This is a problem that can be fixed by making the reactive property a computed property.

For instance, we can write:

```
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <counter></counter>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("counter", {
        data() {
          return {
            count: 0
          };
        },
        provide() {
          return { count: Vue.computed(() => this.count) };
        },
        template: `
          <div>
            <button @click='count++'>increment</button>
            <count-display></count-display>
          </div>
        `
      });

      app.component("count-display", {
        inject: ["count"],
        template: `
          <p>{{count.value}}</p>
        `
      });

      app.mount("#app");
    </script>
  </body>
</html>
```

We have a `counter` component with a `provide` method that returns an object with the `count` property.

Its value is a computed property with `this.count` returned in the callback.

This way, the latest value will be propagated to the child component.

Then in the `count-display` component, we can get the value with the `value` property.

### Conclusion

Provide and inject gives is a way to pass data to deeply nested components without using props.