Building Reusable Components in Vue 3

Building Reusable Components in Vue 3

Building Reusable Components in Vue 3

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.

Step 1 — Strongly Typed Props

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

Step 2 — Flexible Layout with Slots

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

Step 3 — Dependency Injection with provide/inject

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

Step 4 — Expose Component Methods

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