Skip to content

The Sass Module System

This lesson ties together everything from the previous module-related lessons, @use, @forward, @import, and built-in modules, into one coherent mental model of how Sass's module system organizes a real-world project.

How the Pieces Fit Together

The module system has three core ideas: files load other files with @use; files re-export members with @forward; and every loaded file gets its own namespace, preventing the naming collisions that plagued the legacy @import rule.

On top of these three ideas, private members (prefixed with _ or -) let a file hide internal implementation details, giving you a true public/private API boundary inside plain stylesheets.

// _tokens.scss
$_internal-ratio: 1.5; // private, never leaves this file
$spacing-unit: 8px;

@forward 'tokens' hide $_internal-ratio;

// main.scss
@use 'tokens';

.card {
  padding: tokens.$spacing-unit;
}

$_internal-ratio is private by naming convention alone (leading underscore); the explicit hide is technically redundant here, but shown for clarity.

The Three Module Rules at a Glance

@use 'file';       // consume a file's members here
@forward 'file';    // re-export a file's members to consumers
@import 'file';     // legacy, deprecated, avoid in new code
  • @use is for consuming a module's members directly in the current file.
  • @forward is for passing a module's members through to whoever loads the current file.
  • A single file commonly does both: @forward several partials, then @use a couple of them locally too.
  • @import remains for legacy compatibility only and should not appear in new code.

Module System Architecture Cheatsheet

How a typical project structures its module loading.

File Role Typical Content
_tokens.scss Leaf partial Design token variables
_mixins.scss Leaf partial Reusable mixins
abstracts/_index.scss Barrel file @forward for tokens and mixins
components/_buttons.scss Leaf partial Button component styles
components/_index.scss Barrel file @forward for every component
main.scss Entry point @use for abstracts and components

Private Members With a Leading Underscore

Any variable, mixin, or function whose name starts with _ or - is automatically private to the file that defines it. Private members are never forwarded and cannot be accessed via another file's namespace, even without an explicit hide.

// _math-helpers.scss
@function _to-rem($px) {
  @return math.div($px, 16px) * 1rem;
}

@function spacing($px) {
  @return _to-rem($px); // allowed, same file
}

_to-rem is an internal helper; only the public spacing() function is meant to be called from other files.

A Realistic Module Graph

In a real project, main.scss rarely loads dozens of individual partials directly. Instead, it loads a handful of barrel files, each of which forwards its own folder's partials, keeping the entry point short and the folder structure self-contained.

styles/
  abstracts/
    _tokens.scss
    _mixins.scss
    _index.scss    // @forward 'tokens'; @forward 'mixins';
  components/
    _buttons.scss
    _cards.scss
    _index.scss    // @forward 'buttons'; @forward 'cards';
  main.scss        // @use 'abstracts'; @use 'components';

Choosing @use, @forward, or Both

A simple rule of thumb: if a file only exists to organize and re-export other files (a barrel file), use @forward. If a file needs to actually consume another module's variables or mixins to build its own rules, use @use. Many files legitimately need both.

Common Mistakes

  • Manually re-declaring variables in a barrel file instead of using @forward to re-export the originals.
  • Naming an internal helper without a leading underscore, unintentionally making it part of the public API.
  • Building overly deep module graphs that are hard to trace, prefer a shallow barrel-file structure per folder.
  • Mixing legacy @import into an otherwise @use/@forward-based project, reintroducing global scope in just one spot.

Key Takeaways

  • The module system combines @use (consume), @forward (re-export), and namespacing to organize large projects safely.
  • Members prefixed with _ or - are private and never leave the file that defines them.
  • Barrel files (_index.scss) forward an entire folder through one entry point.
  • A typical entry file (main.scss) loads only a few top-level barrel files, not dozens of individual partials.

Pro Tip

Design each folder in your Sass architecture like a small package: a barrel _index.scss file that forwards a curated public API, with private, underscore-prefixed helpers that never leak outside the folder.