# Components Guidelines

> **PRIMARY SKILL FOR CREATING AND UPDATING COMPONENTS** — Guidelines and best practices for writing and modifying Vue 3 components. USE FOR: - Creating new .vue component files - Modifying existing component implementation - Understanding component structure, props, emits, defineModel - Questions about how components should be architected DO NOT USE FOR: - Writing or improving unit tests (use unit-testing-guidelines) - Understanding testing patterns or assertions WHEN TO COMBINE: Load unit-testing-guidelines if you're adding a new component and need to write tests for it.

- Skill: `cmu-sei/components-guidelines` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cmu-sei/components-guidelines`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cmu-sei/components-guidelines/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: cmu-sei (https://skillmd.com/u/cmu-sei)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cmu-sei/components-guidelines

---


## General Component Structure
 - Always use the `<script setup lang="ts">` syntax inside the .vue file for concise code and better developer experience.
 - Use `<template>`, `<script setup lang="ts">`, and `<style scoped>` sections.
 - Use Composition API with reactive `ref`, `reactive`, and `computed` as appropriate.
 - Use `defineOptions` to specify component-level options, such as the `name` property. This helps with debugging, global registration, and tooling support. The `name` should follow the convention: **SdsMyComponent** (PascalCase, prefixed with "Sds").
 - Define props with proper validation and types using `defineProps`. Always type props using TypeScript for improved type safety and developer experience.
 - Define emits using `defineEmits` and document the emitted events.
 - Use slots with clear naming and default slots where applicable.
 - Prefer styling components using Tailwind CSS utility classes within the `<template>`. If dedicated styles are needed, add them to a file in the `tailwindcss` directory located in the parent directory. Use `<style scoped>` inside the `.vue` file only for styles that cannot be achieved with Tailwind as a fallback or last resort.
 - Add suitable accessibility attributes (e.g., `aria-*`) when relevant.
 - All Vue objects are imported globally; explicit imports are not needed.

 ## JavaScript with TypeScript Best Practices
- Follow the TypeScript guidelines and rules defined in [`typescript.instructions.md`](../../instructions/typescript.instructions.md) for type safety, code style, and best practices.
- Code style and quality are enforced using ESLint. Refer to [`eslint.config.js`](../../../eslint.config.js) for linting rules and configuration.

## Component and File Naming Conventions
- Name Vue components using PascalCase (e.g., `MyComponent`).
- Name `.vue` files using PascalCase to match the component name (e.g., `MyComponent.vue`).
- Use descriptive names that reflect the component's purpose.

## Directory Structure Guidelines
- Place components in the `src/components/**/*.vue` directory.
- Store helpers, or utilities, in `src/helpers/*.ts` and Vue composables in a dedicated `src/composables/*.ts` directory.

## Props and Emits
- Use `defineProps<{ propName: type }>()` syntax with TypeScript typings for props.
- Include default values inside props definition or via local fallback where appropriate.
- Use `defineEmits(['eventName'])` or with type annotations for emitted events.
- Document each prop and event with comments to guide code generation.

## Using `defineModel`

`defineModel` is used to create two-way bindings between a parent and child component, replacing the legacy `v-model` and `modelValue` prop pattern. Use it when your component needs to expose a value that can be updated both internally and externally, such as form inputs or toggles.

**When to use:**
- The component should allow its value to be controlled by the parent and also emit updates when changed internally.
- Prefer `defineModel` over manual `modelValue`/`update:modelValue` prop/emits for new components.

**When not to use:**
- If the value is only set by the parent and never updated by the child, use a regular prop.
- For static or read-only data, use props only.

**Code Sample:**
```html
<template>
  <input type="text" :value="textInput" @input="textInput = $event.target.value" />
</template>
```

```typescript
<script setup lang="ts">
const emit = defineEmits(['update:modelValue'])

// Use defineModel for two-way binding
const model = defineModel({ type: String, default: '' })

const textInput = computed({
  get() {
    return model.value
  },
  set(value) {
    model.value = value
    emit('update:modelValue', value)
  }
})
</script>
```

## Reactivity and Lifecycle Hooks
- Use `ref` or `reactive` for state variables to maintain reactive data.
- Use `computed` for derived reactive state that depends on other reactive variables.
- Use `watch` to respond to reactive data changes and trigger side effects.
- Use `watchEffect` for automatically tracking and reacting to all reactive dependencies within its scope, especially when you need to respond to multiple reactive sources without explicitly listing them.
- Use lifecycle hooks like `onMounted`, `onBeforeUnmount`, etc., for component setup and cleanup.
- Prefer template-driven rendering and minimize JS logic outside reactive contexts.

## Template Guidelines
- Use semantic HTML tags where possible.
- Bind props and events clearly, using shorthand (`:` and `@`) where obvious.
- Use `v-if`/`v-show` and `v-for` thoughtfully, always with key attributes in lists.
- Add accessibility (a11y) considerations inside templates, such as `role`, `aria-label`.
- Use slots for extra flexibility and user customization.
- Add a `data-id` attribute with the component name to the template for easier testing and identification (e.g., `data-id="sds-my-component"`).

## Documentation
- Use JSDoc-style comments for props, emits, functions, and composables to improve code readability and maintainability.
- Document each prop and event with a comment describing its purpose, type, and default value (if any).
- Document each slot with a comment describing its purpose and content.
- For composables, include a summary of what the function does, its parameters, and return value.

## Component Registration

To make a component available for use in a Vue application, you need to register it. This is typically done by adding an `install` method to the component and exporting it from an `index.ts` file in the component's directory.

**How it works:**
- The component is imported from its `.vue` file.
- An `install` method is attached to the component, which registers it globally using `Vue.component` and the component's name.
- The component is then exported as the default export.
- After adding the `install` method, export the component from its own directory's `index.ts` file.
- In `src/components/index.ts`, import and re-export all components to enable global registration and simplified imports throughout your project.
- This centralizes component exports, making it easier to manage and use components across your application.

**Example:**
```typescript
import { App } from 'vue'
import Component from './MyComponent.vue'

Component.install = (Vue: App) => {
  Vue.component(Component.name!, Component)
}

export default Component
```

**Why:**
- This pattern allows the component to be installed as a plugin, making it available globally in any Vue app via `app.use(Component)`.
- It supports both local and global registration, improving flexibility and reusability across projects.

---

For more detailed information, refer to the official documentation:

- [Vue.js Official Documentation](https://vuejs.org/)
