Skip to content

Using Props in Vue 3's <script setup>

Vue 3's Composition API introduced the <script setup> syntax, a more concise way to write single-file components. This guide covers how to properly use props with this modern syntax.

Problem Statement

When using <script setup>, you can't access component props the same way as in the Options API. The original question shows a common scenario where developers need to:

  1. Define props using defineProps()
  2. Access those props values within asynchronous functions or reactive logic
  3. Use props in TypeScript with proper type safety

Basic Prop Usage

The simplest way to define props in <script setup> is using defineProps():

vue
<script setup>
const props = defineProps({
  no: String
})

const init = async () => {
  console.log(props.no) // Access props values
}
init()
</script>

Reactive Props Access

If you need to destructure props while maintaining reactivity, use toRefs():

vue
<script setup>
import { defineProps, toRefs } from 'vue'

const props = defineProps({
  no: String,
  myIdOfSomething: Number
})

const { no, myIdOfSomething } = toRefs(props)

console.log(no.value) // Access reactive value
console.log(myIdOfSomething.value)
</script>

WARNING

Directly destructuring props without toRefs() will break reactivity. Always use toRefs() when you need to destructure props.

Default Values

You can define default values for props using the object syntax:

vue
<script setup>
defineProps({
  isOpen: {
    type: Boolean,
    default: true
  }
})
</script>

TypeScript Support

Runtime Declaration

For TypeScript users, you can define props with type safety using Vue's PropType:

vue
<script setup lang="ts">
import { PropType } from 'vue'

defineProps({
  color: {
    type: String as PropType<'primary'|'info'|'success'|'error'|'warning'>,
    default: 'primary'
  }
})
</script>

Type-Only Declaration

For better type inference, use type-only declarations:

vue
<script setup lang="ts">
type Color = 'primary'|'info'|'success'|'error'|'warning'

const props = defineProps<{
  color: Color
  disabled?: boolean
}>()
</script>

Default Values with TypeScript

Use withDefaults() to provide default values with type-only props:

vue
<script setup lang="ts">
interface Props {
  disabled?: boolean
  loading?: boolean
  label?: string
}

const props = withDefaults(defineProps<Props>(), {
  label: "Button Label",
  loading: false,
  disabled: false
})
</script>

Practical Example

Here's a complete component using props with <script setup>:

vue
<template>
  <div class="px-4 w-8/12 sm:w-3/12">
    <img :src="src" :alt="alt" class="border-none rounded-full h-auto max-w-full align-middle" />
  </div>
</template>

<script setup>
const props = defineProps({
  src: String,
  alt: String,
})
</script>
vue
<template>
  <div class="flex flex-wrap justify-center">
    <CircleImage src="/file1.jpg" alt="one" />
    <CircleImage src="/file2.svg" alt="two" />
  </div>
</template>

<script setup>
import CircleImage from '@/components/CircleImage.vue'
</script>

Key Points to Remember

  1. Auto-imports: defineProps and defineEmits are automatically available in <script setup>
  2. Reactivity: Use toRefs() when destructuring props to maintain reactivity
  3. Type Safety: For TypeScript projects, use type-only declarations for better development experience
  4. Defaults: Provide default values either through object syntax or withDefaults()

INFO

The original RFC syntax using <script setup="props, { emit }"> is outdated. Always use defineProps() and defineEmits() in modern Vue 3 applications.

By following these patterns, you can effectively use props in Vue 3's <script setup> syntax while maintaining type safety, reactivity, and clean code organization.