Fix: Sass Error - Undefined Variable
Quick Answer
Fix the Sass undefined variable error caused by @use vs @import migration, variable scope issues, and dart-sass namespace changes with clear solutions.
The Error
You compile your Sass files and get:
Error: Undefined variable.
|
12 | color: $primary-color;
| ^^^^^^^^^^^^^^
src/components/button.scss 12:8 @useOr with older node-sass:
SassError: Undefined variable: "$primary-color"
on line 12 of src/components/button.scssThe variable exists in another file, but Sass can’t find it during compilation.
Why This Happens
Sass variable visibility depends on how you import files. The modern @use rule (introduced in Dart Sass) creates a namespace for each imported file. Variables from @use-imported files aren’t available globally — you must access them through their namespace. This is the most common cause of the error when migrating from @import to @use.
With the legacy @import rule, all variables were global. With @use, they’re scoped. This is intentional — it prevents naming collisions — but it breaks existing code that assumes global access.
Fix 1: Use the Correct Namespace
When you @use a file, its variables are available under a namespace matching the filename:
// _variables.scss
$primary-color: #3b82f6;
$font-size-base: 16px;
// button.scss
@use 'variables';
.button {
// Wrong - no namespace
color: $primary-color;
// Correct - with namespace
color: variables.$primary-color;
font-size: variables.$font-size-base;
}The namespace is the last component of the file path without the extension. For @use 'config/variables', the namespace is variables.
Customize the namespace with as:
@use 'variables' as vars;
.button {
color: vars.$primary-color;
}Or remove the namespace entirely with as *:
@use 'variables' as *;
.button {
color: $primary-color; // Works without namespace
}Common Mistake: Overusing
as *defeats the purpose of@usenamespacing. Reserve it for files you use extensively (like your design tokens). For everything else, keep the namespace to avoid collisions.
Fix 2: Configure @forward for Library Files
If you have an index file that re-exports variables from multiple files, use @forward:
// abstracts/_variables.scss
$primary-color: #3b82f6;
// abstracts/_mixins.scss
@mixin flex-center { display: flex; align-items: center; justify-content: center; }
// abstracts/_index.scss
@forward 'variables';
@forward 'mixins';
// components/button.scss
@use '../abstracts';
.button {
color: abstracts.$primary-color;
@include abstracts.flex-center;
}@forward makes the forwarded module’s members available to anyone who @uses the forwarding file. Without @forward, @use 'abstracts' would only expose members defined directly in _index.scss.
Fix 3: Fix File Import Order
Variables must be defined before they’re used. With @use, the order matters:
// Wrong - using variable before it's available
@use 'components/button'; // Uses $primary-color
@use 'variables'; // Defines $primary-color
// Correct - define first, use second
@use 'variables';
@use 'components/button';However, each file should @use its own dependencies rather than relying on import order in a main file:
// components/_button.scss — self-contained
@use '../variables';
.button {
color: variables.$primary-color;
}This makes each file independent and eliminates order-related issues.
Fix 4: Fix Partial File Naming
Sass partials must start with an underscore. Without it, Sass may not find the file:
styles/
├── _variables.scss ✓ Partial (imported, not compiled alone)
├── variables.scss ✗ Compiled as standalone file
└── main.scssWhen you write @use 'variables', Sass looks for _variables.scss. If the file is named variables.scss without the underscore, Sass treats it as a standalone file, not an importable partial.
Rename your files to include the underscore prefix:
mv variables.scss _variables.scss
mv mixins.scss _mixins.scssFix 5: Migrate from node-sass to Dart Sass
node-sass is deprecated and doesn’t support @use or @forward. If you’re using @import with node-sass, everything is global. But if you switch to Dart Sass without updating your import style, variables break:
# Check which Sass you're using
npx sass --version # Dart Sass
node -e "console.log(require('node-sass').info)" # node-sassMigrate to Dart Sass:
npm uninstall node-sass
npm install sassAfter switching, you have two options:
- Keep using
@import(works but deprecated, will be removed in Dart Sass 3.0) - Migrate to
@use/@forward(recommended)
Use the official migration tool:
npx sass-migrator module style.scssPro Tip: The Sass Migrator tool handles most of the
@importto@useconversion automatically, including adding namespaces and converting variable references. Run it with--dry-runfirst to preview changes.
Fix 6: Fix Load Paths
If your Sass files are in a non-standard directory, Sass may not find them. Configure load paths:
# CLI
sass --load-path=src/styles main.scss output.css
# Webpack (sass-loader)
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths: [path.resolve(__dirname, 'src/styles')]
}
}
}For Vite:
// vite.config.js
export default {
css: {
preprocessorOptions: {
scss: {
includePaths: ['src/styles']
}
}
}
}With load paths configured, you can use shorter imports:
// Instead of
@use '../../styles/variables';
// You can write
@use 'variables';Fix 7: Fix Variable Scope in Control Structures
Variables defined inside control structures (@if, @each, @for) are local to that block:
// Wrong - $color is scoped to the @if block
@if $theme == 'dark' {
$color: #ffffff;
}
.text { color: $color; } // Error: Undefined variable
// Correct - declare outside, assign inside
$color: #000000;
@if $theme == 'dark' {
$color: #ffffff !global;
}
.text { color: $color; }The !global flag is needed to modify a variable from an outer scope inside a control structure. Without it, Sass creates a new local variable.
For mixin arguments, variables are scoped to the mixin body:
@mixin theme($bg, $text) {
background: $bg;
color: $text;
// $bg and $text don't exist outside this mixin
}Fix 8: Configure Global Variables with @forward ... with
Sometimes you need to override default variables from a library. Use @forward ... with to set configurable defaults:
// _config.scss
$primary-color: #3b82f6 !default;
$border-radius: 4px !default;
// _index.scss
@forward 'config';
// main.scss — override defaults
@use 'index' with (
$primary-color: #ef4444,
$border-radius: 8px
);Variables must be marked with !default to be configurable. The with clause only works with @use, not @forward.
For third-party libraries like Bootstrap:
// Override Bootstrap variables before importing
@use 'bootstrap/scss/bootstrap' with (
$primary: #custom-color,
$enable-shadows: true
);Still Not Working?
Check for typos in variable names. Sass variables are case-sensitive.
$Primary-Colorand$primary-colorare different variables.Verify file extensions. Sass processes
.scssand.sassdifferently. Don’t mix syntaxes in@usewithout specifying the extension.Clear your build cache. Stale cached files can cause phantom errors. Delete
.sass-cache/,node_modules/.cache/, and your output directory.Check for circular imports. If file A uses file B and file B uses file A, Sass may fail silently. Restructure to break the cycle — extract shared variables into a separate file.
Verify your bundler configuration. Webpack, Vite, and other bundlers resolve Sass imports differently. Check that your sass-loader or preprocessor options match your file structure.
Test with the Sass CLI directly. Run
npx sass input.scss output.cssto isolate whether the issue is with Sass itself or your build tool’s configuration.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Tailwind CSS Classes Not Applying
How to fix Tailwind CSS classes not applying to your HTML elements. Covers content config paths, purge and safelist, class conflicts and specificity, dynamic class names, PostCSS config, @apply issues, dark mode config, and JIT mode problems.
Fix: ASP.NET 500 Internal Server Error
Fix ASP.NET 500 Internal Server Error by enabling developer exception pages, fixing DI registration, connection strings, and middleware configuration.
Fix: Celery Task Not Received or Not Executing
Fix Celery tasks not being received or executed by resolving broker connections, autodiscovery issues, task name mismatches, and worker configuration.
Fix: Elasticsearch Cluster Health Red Status
Fix Elasticsearch cluster health red status by resolving unassigned shards, disk watermark issues, node failures, and shard allocation problems.