Skip to content

IxDF's Vue.js conventions ​

These conventions are based on the official Vue.js style guide. Where a choice is given between various options, this document will explicitly state which one to pick within the IxDF codebases.

Use multi-word component names ​

User component names should always be multi-word, except for root App components. This prevents conflicts with existing and future HTML elements, since all HTML elements are a single word.

Bad

html
<item />

Good

html
<todo-item />

Always add detailed prop definitions ​

Prop definitions should always be as detailed as possible.

Bad

js
const props = defineProps(['status']);

Good

js
// With TypeScript (recommended)
const props = defineProps<{
    status: string
}>()

// Without TypeScript
const props = defineProps({
    status: {
        type: String,
        required: true,
    }
})

Add a key when using v-for ​

A key with v-for is always required on components, in order to maintain internal component state down the subtree.

Bad

html
<ul>
    <li v-for="todo in todos">{{ todo.text }}</li>
</ul>

Good

html
<ul>
    <li v-for="todo in todos" :key="todo.id">{{ todo.text }}</li>
</ul>

Don't use v-if with v-for ​

Never use v-if on the same element as v-for.

Bad

html
<ul>
    <li v-for="user in users" v-if="user.isActive" :key="user.id">{{ user.name }}</li>
</ul>

Good

html
<ul>
    <template v-for="user in users" :key="user.id">
        <li v-if="user.isActive">{{ user.name }}</li>
    </template>
</ul>

Component files should be named with PascalCase ​

Bad

components/
|- mycomponent.vue
|- myComponent.vue

Good

|- MyComponent.vue

Append "Base" prefix to any foundational components ​

Foundational components, such as buttons, tables or icons that get used within more specific components should all begin with the 'Base' prefix. This ensures they don't clash with HTML element names and also ensures that they are neatly organized together in the codebase.

Bad

components/
|- Button.vue
|- Table.vue
|- Icon.vue

Good

components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue

Tightly coupled component names ​

Child components that are tightly coupled with their parent should include the parent component name as a prefix.

If a component only makes sense in the context of a single parent component, that relationship should be evident in its name. Since editors typically organize files alphabetically, this also keeps these related files next to each other.

Bad

components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue

Good

components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue

Order of words in component names ​

Component names should start with the highest-level (often most general) words and end with descriptive modifying words. This helps make searching for specific components easier and keeps the codebase neatly organised.

Bad

components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue

Good

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue

Component Name Casing ​

Components should always be PascalCase in both .vue and .js files. This has a few advantages over kebab-case:

  • Editors can autocomplete component names in templates, because PascalCase is also used in JavaScript.
  • <MyComponent> is more visually distinct from a single-word HTML element than <my-component>, because there are two character differences (the two capitals), rather than just one (a hyphen).
  • If you use any non-Vue custom elements in your templates, such as a web component, PascalCase ensures that your Vue components remain distinctly visible.

Bad

html
<my-component />

Good

html
<MyComponent />

Full-word component names ​

Don't use abbreviations in component names.

Bad

components/
|- SdSettings.vue
|- UProfOpts.vue

Good

components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

Prop name casing ​

Always use camelCase for prop declarations and within templates

js
const props = defineProps({
    greetingText: String,
});
html
<WelcomeMessage greetingText="hi" />

Multi-attribute elements ​

Elements with multiple attributes should span multiple lines, with one attribute per line.

Bad

html
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo" /> <MyComponent foo="a" bar="b" baz="c" />

Good

html
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo" />

<MyComponent foo="a" bar="b" baz="c" />

Simple expressions in templates ​

Component templates should only include simple expressions, with more complex expressions refactored into computed properties or methods.

Bad

js
{{
    fullName.split(' ').map((word) => {
    return word[0].toUpperCase() + word.slice(1)
    }).join```(' ')
}}

Good

js
<!-- In a template -->
{{ normalizedFullName }}

// The complex expression has been moved to a computed property
const normalizedFullName = computed(() =>
fullName.value
    .split(' ')
    .map((word) => word[0].toUpperCase() + word.slice(1))
    .join(' ')
)

Simple computed properties ​

Complex computed properties should be split into as many simpler properties as possible.

Bad

js
const price = computed(() => {
    const basePrice = manufactureCost.value / (1 - profitMargin.value);
    return basePrice - basePrice * (discountPercent.value || 0);
});

Good

js
const basePrice = computed(() => manufactureCost.value / (1 - profitMargin.value));

const discount = computed(() => basePrice.value * (discountPercent.value || 0));

const finalPrice = computed(() => basePrice.value - discount.value);

Quoted attribute values ​

Non-empty HTML attribute values should always be inside quotes.

Bad

html
<input type="text" /> <AppSidebar :style={width:sidebarWidth+'px'}>

Good

html
<input type="text" /> <AppSidebar :style="{ width: sidebarWidth + 'px' }"></AppSidebar>

Directive shorthands ​

Within the IxDF codebases, directive shorthands (: for v-bind:, @ for v-on: and # for v-slot) should always be used. The rule is to either always use directive shorthands, or never - don't mix and match. We've chosen to use them.

Bad

html
<input v-bind:value="newTodoText" v-bind:placeholder="newTodoInstructions" />

<input v-on:input="onInput" v-on:focus="onFocus" />

<template v-slot:header>
    <h1>Here might be a page title</h1>
</template>

<template v-slot:footer>
    <p>Here's some contact info</p>
</template>

Good

html
<input :value="newTodoText" :placeholder="newTodoInstructions" />

<input @input="onInput" @focus="onFocus" />

<template #header>
    <h1>Here might be a page title</h1>
</template>

<template #footer>
    <p>Here's some contact info</p>
</template>

Single-file component top-level element order ​

Single-File Components should always order <script>, <template>, and <style> tags consistently, with <style> last, because at least one of the other two is always necessary.

Bad

html
<!-- ComponentA.vue -->
<template>...</template>
<script>
    /* ... */
</script>
<style>
    /* ... */
</style>

Good

html
<!-- ComponentA.vue -->
<script>
    /* ... */
</script>
<template>...</template>
<style>
    /* ... */
</style>