Dev Tips

Explaining the Vue Context Argument - A Composition API Tutorial

Matt Maribojoc

Matt Maribojoc · 7 min read

Jun 03, 2021

When working in the Vue 3 Composition API, there are brand new ways to access component functionality. In this article, we’ll be taking a look at the setup function’s context argument.

These changes are necessary because, in the Composition API, we don’t have the same reference to this as we do in the Options API.

In the Options API, we could call console.log(this) in any of the options and get a reference to the component itself – giving us access to its props, computed properties, data, and more.

Accessing component properties in the Options API

javascript
export default {
  props: {
    lastName: String,
  },
  data() {
    return {
      name: "hello",
    };
  },
  created() {
    console.log(this.lastNameModifiers); // props are on `this`
    console.log(this.name); // data is on `this`
    this.createdMethod(); // methods are on `this`
  },
  methods: {
    createdMethod() {
      console.log("created");
    },
  },
};

However, Vue 3 lets us use the Composition API, where all of our code is located inside a setup function. This means that setup is where we declare our reactive data, methods, and computed properties.

Composition API Equivalent

javascript
import { ref } from "vue";
export default {
  props: {
    lastName: String,
  },
  setup() {
    // how do we access props without this??

    const createdMethod = () => {
      console.log("created");
    };
    const name = ref("hello");

    createdMethod();

    return {
      createdMethod,
      name,
    };
  },
};

Setup runs before our component instance is actually created, and since our setup property is where we actually define basically everything for our component, there is no longer a reference to the component itself using this.

So how do we access component properties?

The Composition API gives us alternative ways to access important component information like its props and slots.

This is possible because our setup function takes two properties that let us access some component properties: props and context.

  • context is a Javascript object that exposes three component properties

And these three properties are:

  • context.attrs – the non-prop attributes passed to our component

  • context.slots – an object with all of our template slots’ render functions

  • context.emit – the method for our component to emit events

Let’s take a deeper look at each of these.

context.attrs

Again, context.attrs contains all of the non-prop attributes passed to our component.

What does this mean?

When we actually use our component, any element attribute we add that is not declared in our props will be available inside context.attrs

Say we have a custom component that accepts a prop called value.

ChildComponent using context.attrs

javascript
export default {
  props: {
    value: String,
  },
  setup(props, context) {
    console.log(context.attrs);
  },
};

And then in a parent component, we pass it several attributes.

ParentComponent.vue - passing attributes

markup
<template>
   <custom-component
      :value="value"
      test="hi"
      @close="close"
    />
</template>

The result of our log statement will be:

As you can see, it contains everything besides our declared props. This includes things like event listeners and HTML attributes.

One important note here is attrs is not reactive. Meaning that if we want to apply side effects in response to values of attrs changing, we should use the onUpdated lifecycle hook instead.

context.slots

Next, context.slots is a little confusing, so let’s walk through an example of when it’s useful.

In short, context.slots gives us access to the render method of each of the slots. This is useful when we’re writing our own custom render function, and not using template code.

Vue recommends using templates in a majority of use cases, but if you really want to use the full power of Javascript, we can create our own render functions.

The example in the Vue docs for a great time to use a custom render method is if we are creating a component that renders a slot value with different level heading depending on the value of a prop.

CustomComponent.vue

vue
<template>
  <div>
    <h1 v-if="level == 1">
      <slot />
    </h1>
    <h2 v-if="level == 2">
      <slot />
    </h2>
    <h3 v-if="level == 3">
      <slot />
    </h3>
    <h4 v-if="level == 4">
      <slot />
    </h4>
    <h5 v-if="level == 5">
      <slot />
    </h5>
    <h6 v-if="level == 6">
      <slot />
    </h6>
  </div>
</template>

<script>
  export default {
    props: {
      level: Number,
    },
  };
</script>
 

In this code, we’re using v-if and v-else-if conditionals for all 6 heading options. And as you can see, there’s a lot of duplicate code, and it just looks extremely cluttered.

Instead, we could use the render function to programmatically generate our heading. With the Composition API setup function, that looks like this.

ChildComponent.vue - render function

javascript
import { h } from "vue";
export default {
  props: {
    level: Number,
  },
  setup(props, context) {
    console.log("here");
    return () =>
      h(
        "h" + props.level,
        {} // props and attributes: OPTIONAL
        /* MISSING!! this is where children go, for us our slot */
      );
  },
};

However, how do we get our slots to render??

That’s where context.slots comes into play.

By giving us access to every slot’s render function, we can easily add our slot to our render function. Each slot is accessible by its name and since we did not explicitly name our slot, it’s named default.

ChildComponent.vue - rendering slots!

javascript
import { h } from "vue";
export default {
  props: {
    level: Number,
  },
  setup(props, context) {
    console.log("here");
    return () =>
      h(
        "h" + props.level,
        {}, // props and attributes: OPTIONAL
        context.slots.default() /* Rendering our default slot */
      );
  },
};

Now, if we run this with a simple parent component like this

ParentComponent.vue

markup
<template>
    <child-component :level="1"> 
      Hello World
    </child-component>
</template>

Here’s what our finished DOM will look like.

Fantastic!

So, you likely won’t be using context.slots too often, but when you’re writing complex Javascript render functions, it’s a powerful feature.

context.emit

And finally, context.emit replaces this.$emit as our way to emit events from our component.

This is useful for sending any sort of event, with or without data, to a parent component.

Let’s say we want to create an X button that emits an event called close.

ModalComponent.vue

vue
<template>
  <div>
    <button @click="closeModal">X</button>
  </div>
</template>

<script>
  export default {
    setup(props, context) {
      const closeModal = () => {
        context.emit("close" /* can pass payload here */);
      };
      return {
        closeModal,
      };
    },
  };
</script>
 

Then inside our parent component, we can listen for this close event with the v-on directive.

ParentComponent.vue - listen for close event

markup
<modal-component @close="handleClose" />

For a full guide on using emit in Vue, check out this article or YouTube tutorial!

What don’t we have access to in setup

So far we’ve seen how the Composition API gives us access to four different properties: props, attrs, slots, and emit.

But since setup runs before our component instance is created, we will not have access to these three component properties:

  • data

  • computed

  • methods

These are properties that we declare inside setup itself, but we do not have a built-in way to access a list of all of the data properties, for example.

Final Thoughts

In this article, we’ve learned how the Composition API way of accessing some component properties.

Since we don’t have the same access to this as the Options API, the setup function has two arguments that we can use to access a component’s props, attrs, slots, and emit method.

By using the props and context arguments, we can access powerful component properties and add full functionality to all kinds of Vue projects.

If you have any questions, leave them in the replies below!


Join the LearnVue Community

Every week we send out exclusive content to thousands of developers on our mailing list. 100% Free.

Latest Posts

Top Tools

Making a Markdown-Based Blog with Vue and Gridsome

Use Vue with Gridsome is one of the easiest ways to create static websites from just Markdown files.

Matt Maribojoc · 12 min Read More
Top Tools

5 VueUse Library Functions That Can Speed Up Development

VueUse is an open-source project that provides Vue developers with a huge collection of essential Composition API utility functions for both Vue 2 and Vue 3.

Matt Maribojoc · 12 min Read More
Dev Tips

Lazy Load Components in Vue with defineAsyncComponent

Using Vue 3’s defineAsyncComponent feature lets us lazy load components - meaning they’re only loaded when they’re needed.

Matt Maribojoc · 7 min Read More
Essentials

The Beginner’s Guide to Vue Template Refs - with Vue 3 Updates

Vue Template Refs give our Javascript code a reference to easily access the template.

Matt Maribojoc · 5 min Read More