How to Use Vue 3 Expose in Composition and Options API

Robin
Updated on July 12, 2022

Vue 3 offers many tools to create web applications. You can write flexible and reusable components. In Vue 3.2, it introduced another powerful feature called "Expose".

You can use this expose function in both composition and options API.

In this article, we will learn all about expose function in Vue 3. Most of the developers don't know about this feature. Because there are very few resources on this topic. There are the things you will learn from this post:

  • Why should you use the expose function?
  • How to access this function in the composition API.
  • How to utilize the expose feature in the options API.
  • Exposing component methods in the Vue 3 render function.

Before starting to use this feature in your project let's learn about the usage and why should you use the expose() function in Vue 3?

Usage of Expose Function in Vue 3

In the Vue application, we create multiple components to divide a page into small pieces. In this way, we can reuse one section in many places. It also allows us to update our application very easily.

As your component has a specific purpose, you might not want to access the properties and methods of one component in another. That means you want to keep properties and methods private to the component.

This is where the expose() function comes into play. Using this function you can control which properties and methods of a child component can be called from the parent component.

For example, I have two components one is App.vue another is Todos.vue in my project. When I use Todos.vue inside the App.vue component, Todos.vue is the child and App.vue is the parent component.

          // Todos.vue

<template>
    <section>
        <ul>
            <li v-for="todo in todos" :key="todo.id">{{ todo.title }}</li>
        </ul>
    </section>
</template>

<script>
import { ref } from 'vue';

export default {
    setup() {
        const todos = ref([
            { id: 1, title: 'Learn Vue 3', isCompleted: true },
            { id: 2, title: 'Write a new post', isCompleted: false },
            { id: 3, title: 'Read a book', isCompleted: false },
        ]);

        return {
            todos,
        };
    },
};
</script>
        

Inside the Todos.vue component, I have a property todos which is an array. I display this array of todos in this component. Therefore, there is no need to access property outside this component.

But by default, Vue 3 setup() function disclose all the properties and methods outside the component. You can access all of them from other components.

          // App.vue

<template>
    <main>
        <Todos ref="todosList" />
    </main>
</template>

<script>
import { onMounted, ref } from 'vue';
import Todos from '../components/Todos.vue';

export default {
    components: { Todos },

    setup() {
        const todosList = ref(null);

        onMounted(() => {
            console.log(todosList.value.todos);
        });

        return { todosList };
    },
};
</script>
        

You can see I am using the Todos.vue inside the App.vue component. I have added a ref in the <Todos /> component.

Using the todosList ref, I can access the todos array inside the App.vue component. But you can prevent this to happen using the expose() function in Vue 3.

Before introducing this function, it was very difficult to isolate any properties or methods in a component. Now, you can achieve this by writing a few lines of code.

This is the main purpose of using the expose function in Vue 3. The parent component can only access those properties that you explicitly disclose from the child component.

How to Use Vue 3 Expose in Composition and Options API

Vue 3 Expose Function in Composition API

There 2 types of APIs that you can use to create a component. The composition API is the modern way. It is first added in Vue 3. But you can also use options API if you prefer.

There are some differences between the composition API and the options API in Vue 3 you should know about. In this section, I will show you how to use expose() function in composition API. In the next section, you will see how to do the same thing using options API.

There are also 2 styles available to write components in Vue 3 composition API. You can use the setup() function or you can use the script setup syntax.

I personally like the script setup syntax over the setup() function for a number of reasons. For example, it is the latest feature and allows me to write cleaner code.

But don't worry, I will show you both styles to apply the expose() function.

context.expose() in Setup Function

When you are using the setup() in your component, you have to access the expose() function through the context argument in Vue 3 composition API.

          // Todos.vue

<template>
    <section>
        <ul>
            <li v-for="todo in todos" :key="todo.id">{{ todo.title }}</li>
        </ul>

        <input type="text" v-model="newTodo" />
        <button @click="addTodo">Add Todo</button>
    </section>
</template>

<script>
import { ref } from 'vue';

export default {
    setup(props, context) {
        const todos = ref([
            { id: 1, title: 'Learn Vue 3', isCompleted: true },
            { id: 2, title: 'Write a new post', isCompleted: false },
            { id: 3, title: 'Read a book', isCompleted: false },
        ]);

        const newTodo = ref('');

        const addTodo = () => {
            todos.value.push({
                id: todos.value.length + 1,
                title: newTodo.value,
                isCompleted: false,
            });

            newTodo.value = '';
        };

        const deleteAllTodos = () => {
            todos.value = [];
        };

        context.expose({ deleteAllTodos });

        return {
            todos,
            newTodo,
            addTodo,
            deleteAllTodos,
        };
    },
};
</script>
        

In this example, I have added two functions to the Todos.vue component. The addTodo() function adds new todo and deleteAllTodos() deletes all todos from the todos array.

But I only want to expose the deleteAllTodos() function from this component so that the parent component can access it, but not other properties and methods.

That's why I have called the context.expose() function. As an argument, you have to pass an object will all the properties and methods that you want to expose.

          // App.vue

<template>
    <main>
        <Todos ref="todosList" />
        <button @click="deleteTodos">Delete All</button>
    </main>
</template>

<script>
import { ref } from 'vue';
import Todos from '../components/Todos.vue';

export default {
    components: { Todos },

    setup() {
        const todosList = ref(null);

        const deleteTodos = () => {
            todosList.value.deleteAllTodos();
        };

        return { todosList, deleteTodos };
    },
};
</script>
        

I can call the deleteAllTodos() function using the template ref in the App.vue component. But if I try to access any other methods like addTodo() in the parent, it will throw a TypeError.

Because the Todos.vue component only exposes the deleteAllTodos() function using the expose() function.

If I look inside the todosList ref what properties and methods are present, it will look like this without using the expose function:

Properties and methods in template ref without using expose function in Vue 3 composition api

The todosList ref contains everything from the Todos.vue component. But if we check the same ref by adding the expose() function in the Todos.vue component, it will look like this:

Properties and methods in template ref using expose function in Vue 3 composition api

Now it only contains the exposed function.

Also Read: Improve Performance in Vue 3 Using Lazy Loading and Dynamic Import

defineExpose() in Script Setup Syntax

If you are using the setup syntax in Vue 3 composition API instead of using the setup() function, you will need to follow another technique to apply the expose() function.

In this case, you don't have access to the context argument. But Vue provides defineExpose() helper function.

Unlike the setup() function, the script setup syntax doesn't expose its properties and methods by default. This is the difference between them.

If you don't expose anything from a component that uses the <script setup> syntax, everything will be private. The parent component can not access anything.

          // Todos.vue

<script setup>
import { ref, defineExpose } from 'vue';

const todos = ref([
    { id: 1, title: 'Learn Vue 3', isCompleted: true },
    { id: 2, title: 'Write a new post', isCompleted: false },
    { id: 3, title: 'Read a book', isCompleted: false },
]);

const newTodo = ref('');

const addTodo = () => {
    todos.value.push({
        id: todos.value.length + 1,
        title: newTodo.value,
        isCompleted: false,
    });

    newTodo.value = '';
};

const deleteAllTodos = () => {
    todos.value = [];
};

defineExpose({ deleteAllTodos });

</script>
        

In this example, I want to call the deleteAllTodos() function in the parent component. Therefore, I am importing the defineExpose() function from the vue package.

To expose anything from a <script setup> component, you have to call this defineExpose() function. You can pass an object with all the properties and methods that you want to disclose.

Vue 3 Expose Function in Options API

The options API also exposes all its properties and methods by default like the setup() function. To make your properties and methods private, you have to expose anything explicitly.

To expose any property or method, you have to use the Options API expose property. It's an array. Inside that array, you can add the names of your methods as strings.

          // Todos.vue

<script>
export default {
    data() {
        return {
            todos: [
                { id: 1, title: 'Learn Vue 3', isCompleted: true },
                { id: 2, title: 'Write a new post', isCompleted: false },
                { id: 3, title: 'Read a book', isCompleted: false },
            ],
            newTodo: '',
        };
    },

    methods: {
        addTodo() {
            this.todos.push({
                id: this.todos.length + 1,
                title: this.newTodo,
                isCompleted: false,
            });

            this.newTodo = '';
        },
        deleteAllTodos() {
            this.todos = [];
        },
    },

    expose: ['deleteAllTodos'],
};
</script>
        

In this case, I want to expose the deleteAllTodos() method. Therefore, I have used the expose property with its name.

With this, I have made all other properties and methods inside the Todos.vue private to this component. The parent component will be able to access only the deleteAllTodoes() method.

          // App.vue

<template>
    <main>
        <Todos ref="todosList" />
        <button @click="deleteTodos">Delete All</button>
    </main>
</template>

<script>
import Todos from '../components/Todos.vue';

export default {
    components: { Todos },

    methods: {
        deleteTodos() {
            this.$refs.todosList.deleteAllTodos();
        },
    },
};
</script>
        

Also Read: watch VS watchEffect in Vue 3 - Must Know Differences

Expose Component Methods in Vue 3 Render Function

If you are using the Vue 3 render function in your component, you have to expose properties or methods in order to use them in the parent component.

Because render functions don't expose them by default. I have created a Timer.vue component using the render function.

          // Timer.vue

<script>
import { h, ref } from 'vue';

export default {
    setup(props, context) {
        const timer = ref(0);

        const clearInterval = setInterval(() => {
            timer.value++;
        }, 1000);

        const reset = () => {
            timer.value = 0;
        };

        context.expose({ reset });

        return () => {
            return h('div', [
                h('p', `Timer: ${timer.value}`),
                h('button', { onClick: reset }, 'Reset'),
            ]);
        };
    },
};
</script>
        

This component has the reset() function to reset the timer. In order to use this reset() function in the parent component, you have to expose it using the context.expose() function.

          // App.vue

<script>
import { ref } from 'vue';
import Timer from '../components/Timer.vue';

export default {
    components: { Timer },

    setup() {
        const timer = ref(null);

        const resetFromParent = () => {
            timer.value.reset();
        };

        return {
            timer,
            resetFromParent,
        };
    },
};
</script>
        

Now in the App.vue component, I can call the reset() function using the template ref. But I can not access any other properties or methods from the Timer.vue component.

Conclusion

The expose function is a powerful tool in Vue 3 composition API. It gives us control over our components. You can explicitly expose specific properties and methods from a component.

It is not something that you will use in every component. But it's good to have such features in hand. If your application needs to isolate the components, this is the function you are going to use.

I hope you have learned about the Vue 3 expose function and how to use it inside the composition and options API.

Related Posts