Using Script Setup for Vue 3

With Vue 3, it introduced a <script setup> feature. It is compile-time syntactic sugar for using Composition API in Single File Components. This syntax allows developers to define components without having to export anything from the JavaScript code block. It exposes all its top-level bindings to the template. Long story short, this syntax makes Single File Components simpler and makes you feel you’re working with native JavaScript code.
In the exact words of the RFC, “This proposal’s main goal is reducing the verbosity of Composition API usage inside Single File Components (SFCs) by directly exposing the context of <script setup> to the template.”
Key difference between <script setup> and <script>
- Performance
A <script setup> has better runtime performance as it compiles a template into the render method with the same scope with no intermediate proxy. A <script> compiles a template with an intermediate proxy. - Code Syntax
In a <script> block, we need to export the module with boilerplate code. The <script setup> allows us to define components without having to export anything. In a <script setup> block, we can have more concise code with less boilerplate. - Execution Flow
The <script> block gets executed while we import a component for the first time. The <script setup> block will execute every time an instance of a component gets created. - Organizing Code
We can organize the code as per the business logic inside the <script setup> block. It’s not possible with <script> block, as we’ve to follow the coding structure for the Options API or Composition API of Vue.
Using Components
We only have to import the component to use them in a template, as the compiler automatically adds them to our application. We can use PascalCase or kebab-case for a component name in the template. However, PascalCase’s component names are strongly recommended for consistency. It also helps to differentiate from native custom elements.
(Image — 1) Use the components with <script setup>
Top-level bindings exposed to template
The <script setup> exposes all its top-level bindings (including variables, methods, and imports) to the template. We can directly use an imported helper method in template expressions without having to expose it via the methods option.
(Image — 2) The top-level bindings exposed to the template while using <script setup>
Using Recursive Components
A single File Component can implicitly refer to itself via its filename. For example, a component with a filename as ListRenderer.vue can refer to itself as < ListRenderer /> in its template.
(Image — 3) Recursive component with <script setup>

(Image — 4) Recursive component with <script setup>
Using Dynamic Components
All imported components are referenced as variables, so we can use them with :is binding for dynamic component binding in a template.
(Image — 5) Dynamic component demo with <script setup>
Using Lifecycle Hooks
We can register for a lifecycle hook of the component by using onX methods, which we can import from the vue library.
(Image — 6) Using lifecycle hook inside <script setup>
Reactivity
A Reactive state needs to be created using Reactive APIs. For example, refs automatically unwrapped when referenced in the template.
(Image — 7) Reactivity demo with <script setup>
Using Emits
To define emits, we need to use the defineEmits API method, which fully supports type inference. defineEmits accepts the same value as emits option.
(Image — 8) Using emits with <script setup>
(Image — 9) Using emits with <script setup>
Using Props
To define props, we need to use the defineProps API method, which fully supports type inference. defineProps accepts the same value as the props option.
(Image — 10) Using pops with <script setup>
As per the official documentation, we do not need to import the defineProps and defineEmits inside <script setup>. However, you’ll face a no-undef error if you’ve used them without importing.
Below solutions can help you to overcome the no-undef issue:
- Create Vue project with Vite. (explore more)
(Image — 11) create Vue project with Vite
- Add the below rules in your ESLint configuration file. (explore more)
(Image — 12) ESLint configuration to fix no-undef error
Reacting to Changes with watch
We can react to data changes through the watch option provided by Vue. This is most useful when we want to perform asynchronous or expensive operations in response to changes.
(Image — 13) Using watch with <script setup>
Using Computed Properties
We can declare a computed property that automatically gets updated whenever, depending on property or data changed.
(Image — 14) Using computed properties with <script setup>
Using normal <script> with <script setup>
A <script setup> can be used alongside normal, <script> as the <script setup> creates its own scope for its top-level bindings. Vue will mix all together for you, so your Composition code and Options code remain separate.
A normal <script> is needed in certain cases when we require code that must be executed in module scope. Few pointers like below:
- Declare options that cannot be expressed in <script setup>, for example inheritAttrs
- Custom options enabled via plugins
- Declaring named exports
- Run side effects or create objects that should only execute once
- The framework like Nuxt that provide additional methods to the standard options API syntax that are not exposed in <script setup>
(Image — 15) Using <script> with <script setup>
(Image — 16) Using <script> with <script setup>