Building reusable components is at the heart of Vue development. This article covers the key APIs and patterns for creating components that are flexible, composable, and easy to maintain.
<script setup>
const props = defineProps({
variant: {
type: String,
default: 'primary',
validator: (v) => ['primary', 'secondary', 'danger'].includes(v),
},
size: { type: String, default: 'md' },
disabled: { type: Boolean, default: false },
label: { type: String, required: true },
})
const emit = defineEmits(['click'])
</script>
<template>
<button
:class="[`btn-${props.variant}`, `btn-${props.size}`]"
:disabled="props.disabled"
@click="emit('click', $event)"
>
{{ props.label }}
</button>
</template>
<!-- BaseCard.vue -->
<template>
<div class="card">
<header v-if="$slots.header" class="card-header">
<slot name="header" />
</header>
<main class="card-body"><slot /></main>
<footer v-if="$slots.footer" class="card-footer">
<slot name="footer" />
</footer>
</div>
</template>
// Parent component
import { provide, ref } from 'vue'
const theme = ref('light')
provide('theme', theme)
// Deep child component — no prop drilling needed
import { inject } from 'vue'
const theme = inject('theme', 'light') // second arg is default
<!-- BaseModal.vue -->
<script setup>
import { ref } from 'vue'
const isOpen = ref(false)
const open = () => { isOpen.value = true }
const close = () => { isOpen.value = false }
defineExpose({ open, close })
</script>
<!-- Parent -->
<script setup>
import { ref } from 'vue'
const modal = ref(null)
</script>
<template>
<BaseModal ref="modal" />
<button @click="modal.open()">Open Modal</button>
</template>
All Comments