DEV Community

Cover image for Using script Setup for Vue 3 SFCs
Alvaro Saburido
Alvaro Saburido

Posted on • Edited on

Using script Setup for Vue 3 SFCs

A few days ago I remembered a cool feature that was part of the RFCs that made it into Vue 3 and the composition API final releases when Evan You twitted this:

So I decided to share it in case you also didn't catch up with this nice feature at the time it was announced.

ย What is <script setup>?

First, let's talk about how we normally implement Single Files components (SFCs) when using the Composition API


<template>
  <button class="btn" @click="onClick">{{label}}</button>
</template>

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

export default {
  setup() {
    const label = ref(`I'm a very dangerous button`);

    function onClick() {
      label.value = `Don't touch me`
    }

    return {
      label,
      onClick
    }
  } 
}
</script>

<style>
.btn {
  outline: none;
  border: none;
  background: #5EDCAE;
  padding: 15px;
  border-radius: 5px;
  color: white;
  font-weight:600;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Here we have a very dangerous button that is ready to kick some as** at the best Cobra Kai style.

Cobra Kai

Sorry, I really liked the GIF ๐Ÿ˜…. As you see in the code above we are using the setup method to define the label and a default function when the user clicks our component and we exporting them into the <template /> to be used.

Very often setup is the only option being used when authoring components using the Composition API and yes, one of most often complaints about it is the need to repeat all the bindings that need to be exposed to the render context.

This is where <script setup /> comes to town, with this attribute a new compile step is included where the code runs in the context of the setup() function of the component. Applying that to our Dangerous Button:

<template>
  <button class="btn" @click="onClick">{{label}}</button>
</template>

<script setup>
import { ref } from 'vue';
export const label = ref(`I'm a very dangerous button`);

function onClick() {
  label.value = `Don't touch me`
}
</script>
Enter fullscreen mode Exit fullscreen mode

Looks nicer right? Of course, for such a small component, it's difficult to see the benefit of this, but when components get bigger and bigger, it's appreciated.

Using setup() arguments

What happens if we need to access the props or the context? Just add them as the value of the setup attribute

<template>
  <button class="btn" @click="onClick">{{label}}</button>
</template>

<script setup="props, {emit}">
import { ref } from 'vue';
export const label = ref(`I'm a very dangerous button`);

export function onClick() {
  label.value = `Don't touch me`;
  emit('No Mercy');
}
</script>
Enter fullscreen mode Exit fullscreen mode

Declaring props or additional options

One caveat of <script setup> is that removes the ability to declare other component options, like props. This can be easily solved by treating the default export as additional options like this:

<script setup="props, {emit}">
import { ref } from 'vue';

export default {
  props: {
     color: String,
     default: 'primary' 
  }
}

export const label = ref(`I'm a very dangerous button`);

export function onClick() {
  label.value = `Don't touch me`;
  emit('No Mercy');
}

export const buttonClass = computed(() => `btn-${props.color}`);

</script
Enter fullscreen mode Exit fullscreen mode

Typescript

Would it work with Typescript? It should. To type setup arguments simply declare them

<script setup="props" lang="ts">
import { ref } from 'vue';

declare const props: {
   color: String
}

export const label = ref(`I'm a very dangerous button`);

export function onClick() {
  label.value = `Don't touch me`;
  emit('No Mercy');
}

export const buttonClass = computed(() => `btn-${props.color}`);

</script
Enter fullscreen mode Exit fullscreen mode

Before you go

It's important to highlight that this approach relies on the context of an SFC. script setup> cannot be used with the src attribute if the logic is moved to an external .js or .ts file.

For the sake of safety, make sure you click that ๐Ÿฆ„ or โค๏ธ so we don't make our Dangerous button angrier than it currently is ๐Ÿ˜…. See ya in the comments!

Top comments (11)

Collapse
 
alberto2000 profile image
Riccardo Lardi • Edited

Hi there, I'm getting undefined for emit as well as props when trying to import them with <script setup="props, {emit}"> as proposed. Any hint to what could be wrong? Project was created using @vitejs/app <project-name>... thanks!

Collapse
 
vukadinfe profile image
Vukadin

You can use defineProps, defineEmit and/or defineContext

twitter.com/watchmejizz/status/137...

Collapse
 
vukadinfe profile image
Vukadin

Maybe update post @alvarosaburido ?

Collapse
 
peshanghiwa profile image
Peshang Hiwa

Hi dear, thanks for the amazing article, I had a problem with accessing "this" keyword properties in this new setup as it is undefined,
ex: in my previous projects i could write this.$vuetify.themes.colors.primary,
but now i can't as "this" keyword is undefined,
how to access $vuetify?

Collapse
 
alvarosabu profile image
Alvaro Saburido

Hi there, thanks for writing. So sfc works along with the composition API, is a sugar syntax for the setup function so you will be not able to access $vuetify through this.

Check if Vuetify already supports vue 3 and composition API vuetifyjs.com/en/introduction/road...

Collapse
 
manuelojeda profile image
Manuel Ojeda

This syntax makes every component so clear to read, I love it!

Collapse
 
niuage profile image
Robin Boutros

Hey, I had no idea about that feature! Where's the official documentation about it? I dont see it mentioned in here: v3.vuejs.org/guide/composition-api...

Collapse
 
webjeroen profile image
Jeroen Boschman

Here it is mentioned as a Notable New Feature

Collapse
 
patrikbird profile image
PatrikBird

howโ€™s your IDE/plugin setup for the way of creating components? I tried with code+vetur and itโ€™s just complaining </p>

Collapse
 
patrikbird profile image
PatrikBird

I found an answer myself actually.
Apparently volar works?

github.com/vuejs/vitepress/pull/13...

Collapse
 
nove1398 profile image
nove1398

Great little intro documents, we need more visibility into the SFC style</p>