DEV Community

loading...
Cover image for Using script Setup for Vue 3 SFCs

Using script Setup for Vue 3 SFCs

Alvaro Saburido
"Chamo very crazy loco" Frontend & Creative Telecommunications engineer
Updated on ・3 min read

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!

Discussion (8)

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
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...

Forem Open with the Forem app