Steps for Creating a Component in Vue 3

Note: This guide walks through the complete process of creating reusable Vue 3 components with variants, props, events, and slots.

1. Analyze the UI/UX Design

Before writing any code, carefully examine the design (e.g., Figma, Adobe XD, or mockups) to understand:

This analysis helps in breaking down the component into smaller, reusable pieces.

2. Identify Component Variants

Components often have different variations (colors, sizes, states) that can be controlled via props.

Example: Button Component Variants

Color Variants

Different background and text colors (primary, secondary, success, danger, etc.)

Props to Add:

props: {
    color: {
        type: String,
        default: 'primary', // Default color
        validator: (value) => ['primary', 'secondary', 'success', 'danger'].includes(value),
    }
}

Usage:

<Button color="primary">Submit</Button>
<Button color="danger">Delete</Button>

Size Variants

Different sizes (small, medium, large)

Props to Add:

props: {
    size: {
        type: String,
        default: 'medium',
        validator: (value) => ['small', 'medium', 'large'].includes(value),
    }
}

Usage:

<Button size="small">Small Button</Button>
<Button size="large">Large Button</Button>

State Variations

Different states (disabled, loading, active)

Props to Add:

props: {
    disabled: {
        type: Boolean,
        default: false,
    },
    loading: {
        type: Boolean,
        default: false,
    }
}

Usage:

<Button disabled>Disabled Button</Button>
<Button :loading="true">Loading...</Button>

3. Define Events (Emits)

Components often need to communicate with parent components via custom events.

Example: Button Click Event

When a button is clicked, it should notify the parent component.

Define Emits:

emits: ['button-click'], // Declare custom events
methods: {
    handleClick() {
        this.$emit('button-click'); // Emit event
    }
}

Parent Component Usage:

<Button @button-click="handleButtonClick" />
methods: {
    handleButtonClick() {
        console.log('Button was clicked!');
    }
}

Example: Card Component with Button Event

If a card has a button that triggers an action, emit an event:

<!-- Inside Card Component -->
<button @click="$emit('action-button-click')">Go Somewhere</button>

Parent Usage:

<Card @action-button-click="navigateToPage" />

4. Slots for Dynamic Content

If a component needs flexible content (e.g., a card with variable text), use slots.

Example: Card with Default and Named Slots

<!-- Card.vue -->
<div class="card">
    <slot name="header"></slot> <!-- Named slot -->
    <slot></slot> <!-- Default slot -->
    <slot name="footer"></slot>
</div>

Usage:

<Card>
    <template #header>
        <h3>Card Title</h3>
    </template>
    <p>Main content goes here.</p>
    <template #footer>
        <Button @click="$emit('footer-click')">Action</Button>
    </template>
</Card>

5. Styling (Scoped CSS or CSS-in-JS)

Example: Scoped Styling for Button

<template>
    <button :class="[`btn-${color}`, `btn-${size}`]" @click="handleClick">
        <slot></slot>
    </button>
</template>

<style scoped>
.btn-primary {
    background: blue;
    color: white;
}
.btn-danger {
    background: red;
    color: white;
}
.btn-small { font-size: 12px; }
.btn-large { font-size: 18px; }
</style>

Final Example: Complete Button Component

<template>
    <button
        :class="[`btn-${color}`, `btn-${size}`]"
        :disabled="disabled"
        @click="handleClick"
    >
        <slot></slot>
    </button>
</template>

<script>
export default {
    props: {
        color: {
            type: String,
            default: 'primary',
            validator: (value) => ['primary', 'secondary', 'success', 'danger'].includes(value),
        },
        size: {
            type: String,
            default: 'medium',
            validator: (value) => ['small', 'medium', 'large'].includes(value),
        },
        disabled: {
            type: Boolean,
            default: false,
        },
    },
    emits: ['button-click'],
    methods: {
        handleClick() {
            if (!this.disabled) {
                this.$emit('button-click');
            }
        },
    },
};
</script>

<style scoped>
button {
    padding: 8px 16px;
    border-radius: 4px;
    cursor: pointer;
}
.btn-primary { background: blue; color: white; }
.btn-secondary { background: gray; color: white; }
.btn-success { background: green; color: white; }
.btn-danger { background: red; color: white; }
.btn-small { font-size: 12px; }
.btn-medium { font-size: 16px; }
.btn-large { font-size: 20px; }
button:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}
</style>

Summary

  1. Analyze Design → Break into reusable parts
  2. Define Variants → Use props (color, size, state)
  3. Handle Events → Use emits for parent communication
  4. Use Slots → For dynamic content
  5. Apply Scoped Styles → Avoid CSS conflicts

This approach ensures reusable, maintainable, and scalable Vue components. 🚀