Essentials

Explaining The New script setup Type in Vue 3 - RFC Takeaways

Matt Maribojoc

Matt Maribojoc · 6 min read

May 13, 2021

If you’ve been working in Vite and Vue 3 recently, you’ll notice that when you start a new project, your script section looks like this with this script syntax in your Vue components.

Starter Code

vue
<script setup>
  import HelloWorld from "./components/HelloWorld.vue";
  // This starter template is using Vue 3 experimental <script setup> SFCs
  // Check out https://github.com/vuejs/rfcs/blob/script-setup-2/active-rfcs/0000-script-setup.md
</script>
 

You may be wondering, “What is this? Is this the Options API? Composition API? where’s the setup method?”

The <script setup> type is a proposed change in the Vue’s Git RFCs. To be clear, this is not intended to completely replace any of the current ways to write code. Its purpose is to provide developers with a more concise syntax to write their single file components.

In this article, we’re going to be taking a look at exactly how it works and some of the ways that it can be useful.

Alright – let’s go.

A rundown of script setup

In <script setup>, we don’t have to declare an export default and a setup method – instead, all top-level bindings are exposed to the template

In the Composition API, we’re used to having to create our setup method and then return anything that we want exposed. Something like this…

App.vue

vue
<script>
  import { ref, computed } from "vue";
  export default {
    setup() {
      const a = ref(3);
      const b = computed(() => a.value + 2);

      const changeA = () => {
        a.value = 4;
      };
      return { a, b, changeA }; // have to return everything!
    },
  };
</script>
 

But with <script setup>, we can rewrite the same code like this..

App.vue

vue
<script setup>
  import { ref, computed } from "vue";
  // all of these are automatically bound to the template
  const a = ref(3);
  const b = computed(() => a.value + 2);

  const changeA = () => {
    a.value = 4;
  };
</script>
 

And it’s not just data, computed properties, and methods! Even imported directives and components that are on the top level of our setup scope are automatically available in our template.

Look at this example of importing a component.

Importing Components

vue
<template>
  <component-b />
</template>
<script setup>
  import ComponentB from "./components/ComponentB.vue"; // really that's it!
</script>
 

Amazing, right?

So….what’s the point of this?

Long story short, this syntax makes single file components simpler.

In the exact words of the RFC, “the proposal’s main goal is reducing the verbosity of Composition API usage inside SFCs by directly exposing the context of <script setup> to the template.”

The proposal’s main goal is reducing the verbosity of Composition API usage inside SFCs by directly exposing the context of <script setup> to the template.”Vue script setup rfc

And that’s exactly what we just saw, by not having to worry about creating a setup method and returning exactly what we want to expose, we can simplify our code.

Plus – there’s no matter forgetting to return something from our setup method (something I know I do all the time).

More advanced usage of <script setup>

Now that we know what <script setup> even is and why it can be useful, let’s take a look at some of its more advanced features.

Accessing props, emitting events, etc.

First off, you may be wondering how to perform standard Vue operations like….

  • accessing our context object.

In the Composition API, these were simply arguments on our setup method,

All these were arguments on setup()

javascript
export default {
  setup(props, context) {
    // context has attrs, slots, and emit
  },
};

However, in the script setup syntax, we can access these same options with 3 imports from Vue.

  • defineProps – as the name suggests, allows us to define props for our component

  • defineEmits – lets us define the events that our component can emit

  • useContext – gives us access to the slots and attributes of our component

Now, we can import these methods

vue
<template>
  <button @click="$emit('change')">Click Me</button>
</template>
<script setup>
  import { defineProps, defineEmit, useContext } from "vue";

  const props = defineProps({
    foo: String,
  });
  const emit = defineEmit(["change", "delete"]);

  const { slots, attrs } = useContext();
</script>
 

With these 3 imports we can get the functionality that we’re used to having on our traditional setup method.

Creating an async setup function

Another cool feature of the script setup is how easy it is to create an async setup function.

This is useful for loading in apis as your component is created, and even tying in your code to the experimental <suspense> feature.

All we have to do to make our setup function asynchronous, is use a top level await inside our script setup.

For example, if we’re using the Fetch API, we can just use await like this…

It's this easy to make an async setup()

vue
<script setup>
  const post = await fetch(`/api/pics`).then((a) => a.json())
</script>
 

…and our resulting setup() function will be asynchronous just like that.

It’s that simple.

Using <script setup> with a normal <script>

<script setup> creates its own script scope for its top level bindings. But in certain cases, there is code that must be executed in the module scope.

The 2 specific examples in this RFC are…

  • Declaring named exports

  • Creating global side effects that only execute once.

This can be done by adding a normal <script> block alongside your script setup like this.

Using <script setup> and <script>

vue
<script>
  performGlobalSideEffect();

  // this can be imported as `import { named } from './*.vue'`
  export const named = 1;
</script>

<script setup>
  // code here
</script>
 

And there you have it!

Currently, this script setup is opt-in only so if you want to try it out, just add setup to your script tag.

OR…

If you never want to think about it and just want to write your code the way you’re used to, go for it. The choice is yours.

To learn more about the script setup, here’s the link to the full RFC with its motivations, exact syntax, and more technical implementations.

So that’s all for this article, I hope it helped clear up what this new syntax that’s inside your Vite app!

If you have any questions, leave them in the comments 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