---
title: "Understanding the new script setup with (defineProps & defineEmits) in Vue 3.2"
description: "Learn the fundamentals of Vue 3.2 script setup feature with special APIs like defineProps and defineEmits"
source: "https://www.netlify.com/blog/understanding-defineprops-and-defineemits-in-vue-3.2/"
last_updated: "2026-08-24T11:55:10.000Z"
---
Vue 3 introduced us to the Composition API - a different way of handling reactive data using `ref` and `reactive` in Vue.js. It received a lot of positive feedback but also some concerns about the verbosity of the Composition API usage inside SFCs. Then comes Vue 3.2 with a stable release of a new feature `<script setup>` which aims to address those concerns in a very practical way.

Why am I excited about `<script setup>`? It made the already simple Composition API even simpler. With `<script setup>`, we don’t need named or default exports in SFCs anymore, we can simply define variables and use them in the template.

### Getting started with script setup

This is probably the easiest part. To start using `<script setup>`, all you need to do is add `setup` to your existing script tag like this:

```
<script setup>  import { ref } from 'vue'
  const name = ref('Joe')</script>
```

And you can access name in the template like this:

```
<script setup>  import { ref } from 'vue'
  const name = ref('Joe')</script>
<template>  <h1>{% raw %}{{ name }}{% endraw %}</h1></template>
```

That’s it. This is simple and very useful in reducing code bulk in larger codebases. To achieve the same result with the Composition API, we’ll need to return the variable (i.e expose it to the template) like this:

```
<script>import { ref } from 'vue'
export default {  setup() {    const name = ref('Joe')
    return {      name    }  }}</script>
<template>  <h1>{% raw %}{{ name }}{% endraw %}</h1></template>
```

You can immediately get a sense of how large a codebase can grow when you have large SFCs. With `script setup`, we get rid of all the unnecessary boilerplate code and trim our component down to only what is needed.

Note, we used a `name` variable for simplicity in the `script setup` snippet, but you’re not limited to just variables. Anything (including helper functions) declared inside the `script setup` context will be accessible from the template.

```
<script setup>import { greet } from './utils/greeting'</script>
<template>  <div>{% raw %}{{ greet('good morning!') }}{% endraw %}</div></template>
```

But that’s not all, we can also import and use components without any extra config or bindings. For instance, consider importing a `<SubscriptionForm />` component into the `App.vue` file. Without the `script setup` feature, you’ll probably do something like this:

App.vue

```
<script>  import SubscriptionForm from './components/SubscriptionForm'
  export default {    components: { SubscriptionForm },    setup(){      return {}    }  }</script>
<template>  <SubscriptionForm /></template>
```

This is the pattern I imagine you would be familiar with. But it gets better. With `script setup` you can import the component and use it in the template with no additional step, like this:

App.vue

```
<script setup>  import SubscriptionForm from './components/SubscriptionForm'
</script>
<template>  <SubscriptionForm /></template>
```

We didn’t need to declare a `components` object and register the `SubscriptionForm` component in it as we did before. Cool right?

A good question to ask at this point would be, so we’ve imported the `SubscriptonForm />` component into `App.vue`, how do we pass props to it? or emit an event from it to tell the parent that the form has been submitted? Let’s find out.

The `script setup` feature ships with the `defineProps` and `defineEmits` APIs that make it possible for us to declare props and emits. They are automatically available inside the `script setup` context and doesn’t need to be imported to use them. Let’s demonstrate!

First, in the `<SubscriptionForm />` component, lets define a prop and also emit the `submit` event like so:

SubscriptionForm.vue

```
<template>  <form @submit.prevent="subscribe" v-if="props.age">    <label>Email      <input v-model="email" type="email"/>    </label>    <button>Subscribe</button>  </form></template>
<script setup>import { computed } from "@vue/reactivity"
const props = defineProps({  age: {    type: Number,    required: true  }})const email = computed({  get() {    return props.email  },  set(value) {    emit('subscribe:user', value)  }})
const emit = defineEmits(['subscribe:user', 'subscribe'])
function subscribe() {  emit('subscribe')}</script>
```

In the snippet above, we did a couple of things using the `defineProps` and `defineEmits` API:

-   defineProps – allows us to define props for our component. We used it to define the `age` prop that will get passed in from the parent (App.vue). Our form will only be visible IF the age prop exists.
    
-   defineEmits – lets us define the events that our component can emit. In this case, we emit a `subscribe` event to let the parent component (App.vue) know when the form has been submitted. When that happens, we just log “form submitted” on the parent.
    

Next, lets update App.vue to capture all these code updates:

App.vue

```
<script setup>  import SubscriptionForm from './SubscriptionForm'
  function subscribeUser() {    console.log("form submitted! do something");  }</script>

<template>  <SubscriptionForm age=12  @submit="subscribeUser" /></template>
```

Here, we pass the `age` prop to the `SubsbscriptionForm />` component and set up our `submit` event to call the `subscribeUser` function whenever the form is submitted from the child component.

And that is how we use props and events in the `script setup` context.

### Worthy mentions

As you can imagine, other features shipped with `script setup` that we did not get to in this post. However, one that I find really worthy of mentioning is the dynamic components feature. It allows you to dynamically render components in your Vue templates when certain conditions are satisfied.

```
<script setup>import Profile from './Profile.vue'import LoginForm from './LoginForm.vue'</script>
<template>  <component :is="userLoogedIn ? Profile : LoginForm" /></template>
```

If you’d like to explore more features and learn other things about the Vue `script setup` feature, feel free to read the [documentation here](https://v3.vuejs.org/api/sfc-script-setup.html#basic-syntax).

### Share

-   [X (fka Twitter)](https://twitter.com/intent/tweet?text=Understanding the new script setup with defineProps & defineEmits in Vue 3.2&url=https://www.netlify.com/blog/understanding-defineprops-and-defineemits-in-vue-3.2/)
-   [LinkedIn](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fwww.netlify.com%2Fblog%2Funderstanding-defineprops-and-defineemits-in-vue-3.2%2F)
-   [Facebook](https://www.facebook.com/sharer.php?u=https://www.netlify.com/blog/understanding-defineprops-and-defineemits-in-vue-3.2/)
-   [Bluesky](https://bsky.app/intent/compose?text=Understanding the new script setup with defineProps & defineEmits in Vue 3.2+https://www.netlify.com/blog/understanding-defineprops-and-defineemits-in-vue-3.2/)

* * *

### Tags

-   [Vue 3](/blog/tags/vue-3/)
-   [composition-api](/blog/tags/composition-api/)

## Keep reading

![](/images/blog-fallback-thumbnail.svg)

Guides & Tutorials January 29, 2021

[

### Deep dive into the Vue Composition API’s watch() method

](/blog/2021/01/29/deep-dive-into-the-vue-composition-apis-watch-method/)

-   ![Profile picture of Ekene Eze](/_astro/c97b196f1227b05d92404625ecc4779ce8a60661-1000x1000_Z1bjliB.webp)
    
    Ekene Eze
    

![](/images/blog-fallback-thumbnail.svg)

Guides & Tutorials March 10, 2020

[

### Reactivity in Vue 3

](/blog/2020/03/10/reactivity-in-vue-3/)

-   ![Profile picture of Divya Tagtachian](/_astro/b9b198e6e1c100a667f1b9b97d1b39327587073f-80x80_Z2ehpic.webp)
    
    Divya Tagtachian
    

## Recent posts

News & Announcements August 19, 2026

[

### New clarifying questions in Agent Runners

](/blog/new-clarifying-questions-in-agent-runners)

-   ![Profile picture of Taylor Barnett-Torabi](/_astro/cf4624da8c1286738397b6fecb53bad504df140b-400x400_ZsvK0s.webp)
    
    Taylor Barnett-Torabi
    

Opinions & Insights August 14, 2026

[

### The full power of Git, without the friction: A conversation with Netlify CTO Dana Lawson

](/blog/netlify-source-with-netlify-cto-dana-lawson)

-   ![Profile picture of Dana Lawson](/_astro/856bf146d0c05c9dc25d45b59f7eac955fbbd644-512x512_1n84rs.webp)
    
    Dana Lawson
    

Opinions & Insights August 12, 2026

[

### Choosing an AI model: one prompt, 11 models, very different results

](/blog/one-prompt-11-models-very-different-results)

-   ![Profile picture of Elad Rosenheim](/_astro/be563e8998105c7e95b4db9110fa16b47cb68acd-230x230_Z2hYOHp.webp)
    
    Elad Rosenheim
    

![](/_astro/3f255b372fa958df35802666ee33b4609b2d71bd-1200x1586_1VtE2D.webp)

### How do the best dev and marketing teams work together?

[Access the report](https://www.netlify.com/reports/2024-leadership-trend-report/access/)