@extend vs Mixin
Choose the right reuse tool for the job.
Both mixins and @extend reuse styles, but they differ in output and flexibility.
Rule of thumb
- Need arguments or a
@contentblock? Use a mixin. - Sharing a fixed set of declarations across many selectors? Use
@extendwith a placeholder. - When unsure, prefer mixins — modern minification (gzip) handles the repetition well and mixins avoid @extend's surprises.
Example
// Mixin: parameterized, duplicates output
@mixin pill($bg) {
border-radius: 999px;
background: $bg;
}
.tag { @include pill(#CC6699); }
// Extend: fixed styles, shared once via placeholder
%pill-base { border-radius: 999px; padding: 2px 10px; }
.chip { @extend %pill-base; background: #6699CC; }When to use it
- A developer uses a mixin for button variants because each variant needs different argument values (colors), while using @extend for a shared focus ring because it is always identical.
- A team prefers mixins over @extend in component libraries because @extend cannot cross file boundaries when using @use, but mixins can.
- A developer reviews their codebase and converts three identical %card rules to a mixin when they discover the rules needed to accept a border-color argument.
More examples
When to use @extend
Identical output with no variation is the ideal case for @extend; the placeholder groups both selectors into one CSS rule.
// Good @extend candidate — identical output, no arguments needed
%visually-hidden {
position: absolute;
width: 1px; height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
.skip-link { @extend %visually-hidden; }
.sr-label { @extend %visually-hidden; }When to use a mixin
Because each call produces different property values, a mixin is the right tool — @extend cannot accept arguments.
// Good mixin candidate — output varies by argument
@mixin button($bg, $text: white) {
background: $bg;
color: $text;
padding: 0.5rem 1rem;
border-radius: 4px;
}
.btn-primary { @include button(#2563eb); }
.btn-danger { @include button(#dc2626); }Extend fails across @use modules
Shows that @extend cannot cross @use module boundaries, making mixins the only option for shared styles between separate files.
// _base.scss
%card { border: 1px solid #ccc; }
// components.scss — this will ERROR
@use 'base';
.widget { @extend base.%card; } // Not valid cross-module extend
// Solution: use a mixin instead
@mixin card { border: 1px solid #ccc; }
.widget { @include card; } // Works fine
Discussion