Back to Home

BEM in Vue: styles for components

The article breaks down the use of BEM in Vue for structured styles. Examples of buttons with modifiers, advantages of scoped and scaling rules. Suitable for middle/senior developers.

BEM + Vue: reliable styling system
Advertisement 728x90

BEM in Vue: Structuring Styles for Scalable Components

BEM remains relevant in Vue projects thanks to its strict class hierarchy, which prevents style conflicts in component-based architecture. Vue handles reactivity and logic, while BEM ensures blocks and elements stay independent. This combo minimizes CSS spaghetti as your app grows.

BEM is built on three core principles:

  • Block: A standalone entity like .header or .card, with no external dependencies.
  • Element: A sub-part of a block, denoted by double underscore — .button__icon.
  • Modifier: A variant or state via single underscore — .button_disabled, .card_theme_dark.

This structure isolates styles, making debugging and refactoring a breeze.

Google AdInline article slot

Styling in Vue Components

In Vue, styles go inside <style scoped>, limiting their scope to the component. Scoped prevents style leaks, but watch out for deep nesting.

Use :deep() only for integrating external libraries where scoped falls short. Avoid it to hack your own architecture — that's a red flag for structural issues.

BEM pairs perfectly with scoped by logically grouping classes. The block sets the base style, modifiers handle variations, and elements manage internal structure.

Google AdInline article slot

Practical Example: Button with States

Without a system, classes multiply chaotically. Here's a typical pitfall:

<template>
  <button :class="{
    'button-basic': isBasic,
    'button-href': isHref,
    'button-avatar': isAvatar 
  }">
    {{ text }}
  </button>
</template>

Problems:

  • No hierarchy: classes aren't tied to a block.
  • Scalability: new states mean more conditions.
  • Readability: logic gets lost over time.

With BEM, the template simplifies and self-documents:

Google AdInline article slot
<template>
  <button :class="[
    'button',
    {
      'button_disabled': disabled,
      'button_loading': loading,
    },
    {
      'button_size_xl': size === 'xl',
      'button_size_l': size === 'l',
    },
    {
      'button_primary': color === 'primary',
      'button_secondary': color === 'secondary',
    }
  ]">
    {{ text }}
  </button>
</template>

Classes are grouped by semantics: base block, states, sizes, themes. In CSS:

.button {
  /* base styles */
  padding: 12px 24px;
  border: none;
  border-radius: 4px;
}

.button_disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.button_loading {
  position: relative;
}

.button_primary:not(.button_disabled):hover {
  background-color: #0055ff;
}

The :not() selector filters conflicting states without extra classes.

Scaling and Team Collaboration

In large projects, BEM cuts cognitive load: devs see the block and its modifiers right in the template. No digging through CSS to understand styles.

Team benefits:

  • Predictability: class names reflect purpose.
  • Isolation: blocks don't interfere with each other.
  • Extensibility: add new modifiers without refactoring.

Example of a complex component — product card with themes and sizes:

<template>
  <div class="card" :class="[
    `card_size_${size}`,
    `card_theme_${theme}`
  ]">
    <div class="card__image"></div>
    <div class="card__title">{{ title }}</div>
    <button class="card__button button">Buy</button>
  </div>
</template>

Elements inside the block inherit context but stay independent.

When to Skip BEM

BEM is overkill for prototypes or landing pages: Tailwind or utility classes are faster. Stick to BEM for long-term projects with teams and complex UIs.

Key Takeaways

  • BEM complements Vue with strict hierarchy, no clashes with scoped styles.
  • Modifiers group states and variants, simplifying templates.
  • :deep() is for external libs only, not internal hacks.
  • In big projects, BEM speeds up onboarding and refactoring.
  • Test it on a real component: readability improves instantly.

— Editorial Team

Advertisement 728x90

Read Next