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.
Components often have different variations (colors, sizes, states) that can be controlled via props.
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>
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>
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>
Components often need to communicate with parent components via custom events.
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!');
}
}
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" />
If a component needs flexible content (e.g., a card with variable text), use 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>
<style scoped>
) to avoid CSS conflicts<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>
<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>
emits
for parent communicationThis approach ensures reusable, maintainable, and scalable Vue components. 🚀