Skip to content
Back to skills

Conventions Vue

ASecurity

Apply when working with Vue components, composables, stores, or styling. Ensures code matches established project patterns.

  • 666 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 8, 2026
developmenttypescriptvueapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill conventions-vue --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Conventions Vue?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Conventions Vue
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-conventions-vue/badge)](https://www.skillsdirectory.com/skills/majiayu000-conventions-vue)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: conventions-vue
description: Apply when working with Vue components, composables, stores, or styling. Ensures code matches established project patterns.
---

# Vue Conventions

## Component Structure

**Always:** `<script setup lang="ts">` — no Options API, no class components, no mixins.

### Props

```typescript
// Library components (ui-vue): export interface for reuse
export interface BaseInputProps {
  modelValue: string | number;
  type?: "text" | "email" | "number";
  disabled?: boolean;
}
const props = withDefaults(defineProps<BaseInputProps>(), {
  type: "text",
});

// App components: local interface
interface Props {
  status: string;
  variant?: "solid" | "outline";
}
const props = withDefaults(defineProps<Props>(), {
  variant: "solid",
});
```

### Emits

```typescript
// Typed tuple syntax
const emit = defineEmits<{
  "update:modelValue": [value: string];
  focus: [];
}>();

// v-model via defineModel
const open = defineModel<boolean>("open", { required: true });
```

## File Organization

## Components high-level directory (atomic design)

- `atoms/` → `Base*` prefix (BaseButton, BaseInput, BaseCard)
- `molecules/` → Descriptive (FormControl, TabsGroup)
- `organisms/` → Complex (DataTable)
- `layouts/` → FlexLayout, ModalContainer, SlideOut

## Domain high-level directory

- `domain/common/` → Shared
- `domain/{feature}/` → Feature-specific, ie: domain/radar/

## Other high-level directories

- `composables/` → Feature hooks
- `stores/` → Pinia
- `services/` → API layer
- `types/` → TypeScript definitions

## Composables

**Naming:** `use*` prefix — `useModal`, `useAirportAutocomplete`

**Return pattern:** Object with refs, computed, methods

```typescript
export const useAirportAutocomplete = () => {
  const results = ref<Airport[]>([])
  const loading = ref(false)
  const error = ref<string | null>(null)

  const search = async (term: string) => { ... }

  return { results, loading, error, search }
}
```

**Options for complex composables:**

```typescript
interface UseModalSubmissionOptions<T, R> {
  defaultFormData: T;
  submitAction: (data: T) => Promise<R>;
  onSuccess?: (result: R) => void;
}
```

## Pinia Stores

**Options API style** (not setup stores):

```typescript
export const useSessionStore = defineStore('session', {
  state: (): SessionState => ({
    user: null,
    isLoading: false,
    error: null
  }),
  actions: {
    async checkSession() { ... },
  }
})
```

- Explicit state interfaces
- All API calls in actions
- URL as source of truth for query state

## Styling

See **conventions-css** skill. Key points:

- `<style scoped>` for domain components, unscoped for atomic design components
- Design tokens via CSS custom properties exclusively
- No Tailwind, utility classes, or CSS-in-JS

## Common Patterns

**Loading/error states:**

```vue
<FlexLayout v-if="isLoading" align="center">
  <BaseIcon id="spinner" inline /> Loading...
</FlexLayout>
<AlertMessage v-else-if="error" :message="error" type="error" />
<template v-else>
  <!-- Content -->
</template>
```

**Forms:** FormControl molecule wrapping atoms

```vue
<FormControl v-model="formData.reason" :error="error" label="Reason" type="textarea" />
```

**Modals:** ModalContainer + useModalSubmission

```vue
<ModalContainer :is-open="open" title="Suspend" @close="open = false">
  <FormControl v-model="formData.reason" />
  <template #footer>
    <BaseButton :loading="isLoading" @click="submitForm">Submit</BaseButton>
  </template>
</ModalContainer>
```

**Layout:** FlexLayout/FlexItem, not raw flexbox

```vue
<FlexLayout direction="column" gap="xs">
  <FlexItem :span="6">Label</FlexItem>
  <FlexItem :span="6">Value</FlexItem>
</FlexLayout>
```

## TypeScript

- Strict mode
- `type` for object shapes (props, state)
- `type` for unions and aliases

## Never Do

- Options API
- Mixins
- Global components (always explicit imports)
- Vuex
- Inline styles (except dynamic values)
- Magic strings for events
- CSS-in-JS or Tailwind

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.