Injecting variables during SASS compilation with Node Injecting variables during SASS compilation with Node express express

Injecting variables during SASS compilation with Node


I found myself in a very similar situation. We had a lot of existing SASS that now needed to accept dynamic values/variables to be used throughout (as variables). I originally went down the route of writing temporary directories/files and essentially creating a "proxy entry point" which would create a proxy_entry.scss and variables.scss and bootstrap the actual entry.scss with intended SASS variables declared. This worked fine and achieved the desired results, but it felt a bit overcomplicated...

It turns out there is a much simpler solution available thanks to node-sass's options.data option. This accepts a "SASS string to be evaluated".

Type: String Default: null Special: file or data must be specified

A string to pass to libsass to render. It is recommended that you use includePaths in conjunction with this so that libsass can find files when using the @import directive.

This completely eliminated the need for writing/managing all of the temporary directories and files.

Visual TL;DR

Dynamic Variables in SASS with node-sass

The solution boils down to something like this

1.) Define sassOptions as usual

var sassOptionsDefaults = {  includePaths: [    'some/include/path'  ],  outputStyle:  'compressed'};

2.) Write the "dynamic SASS string" for options.data

var dataString =  sassGenerator.sassVariables(variables) +  sassGenerator.sassImport(scssEntry);var sassOptions = _.assign({}, sassOptionsDefaults, {  data: dataString});

3.) Evaluate the SASS as usual

var sass = require('node-sass');sass.render(sassOptions, function (err, result) {  return (err)    ? handleError(err);    : handleSuccess(result.css.toString());});

Note: this is assuming your entry.scss imports some variables.scss that defines variables as "defaults".

// variables.scss$someColor: blue !default;$someFontSize: 13px !default;// entry.scss@import 'variables';.some-selector {   color: $someColor;  font-size: $someFontSize;}

Piecing it all together as an example

var sass = require('node-sass');// 1.) Define sassOptions as usualvar sassOptionsDefaults = {  includePaths: [    'some/include/path'  ],  outputStyle:  'compressed'};function dynamicSass(scssEntry, variables, handleSuccess, handleError) {  // 2.) Dynamically create "SASS variable declarations"  // then import the "actual entry.scss file".  // dataString is just "SASS" to be evaluated before  // the actual entry.scss is imported.  var dataString =    sassGenerator.sassVariables(variables) +    sassGenerator.sassImport(scssEntry);  var sassOptions = _.assign({}, sassOptionsDefaults, {    data: dataString  });  // 3.) render sass as usual  sass.render(sassOptions, function (err, result) {    return (err)      ? handleError(err);      : handleSuccess(result.css.toString());  });}// Example usage.dynamicSass('some/path/entry.scss', {  'someColor': 'red',  'someFontSize': '18px'}, someSuccessFn, someErrorFn);

Where the "sassGenerator" functions could looks something like

function sassVariable(name, value) {  return "$" + name + ": " + value + ";";}function sassVariables(variablesObj) {  return Object.keys(variablesObj).map(function (name) {    return sassVariable(name, variablesObj[name]);  }).join('\n')}function sassImport(path) {  return "@import '" + path + "';";}

This enables you to write your SASS just as you did before, using SASS variables anywhere that they are needed. It also doesn't tie you down to any "special dynamic sass implementation" (i.e. this avoids using "underscore/lodash templating throughout your .scss files). It also means you can take advantage of IDE features, linting, etc... just the same since you are now just back to writing regular SASS.

Additionally, it translates nicely to non-node/http/compile-on-the-fly usages such as pre-compiling multiple variations of entry.scss given multiple value sets via Gulp, etc...

I hope this helps you @ChrisWright (and others) out! I know I struggled finding information on the subject and I imagine this is a fairly common use-case (wanting to pass dynamic values into SASS from a Database, config, HTTP parameters, etc...).


I was able to solve this after I wrapped my head around node-sass' importer() method. My solution involved underscore templates and manually reading files as they come in. It's not the most elegant or efficient solution, but it is only run once per generated page. After that, files are minified and cached for future requests.

// Other none-sass render parameters omitted for brevityimporter: function (url, prev, done) {    // Manually read each partial file as it's encountered    fs.readFile(url, function (err, result) {        if (err) {            // If there is an error reading the file, call done() with            // no content to avoid a crash            return done({                contents: ''            });        }        // Create an underscore template out of the partial        var partial = _.template(result.toString());        // Compile template and return its contents to node-sass for        // compilation with the rest of the SCSS partials        done({            contents: partial({ data: YOUR_DATA_OBJECT })        });    });}

Using this solution, we are able to reference normal underscore variable syntax inside our SCSS partials. As an example:

body {    color: <%= data.colour %>;}


I was solving a similar problem though not in Node but in Java. I was required to render SASS variables from database to generate website theme, based on client that is accessing the website.

I explored some solutions and came across this third party service https://www.grooveui.com. It offers a language agnostic solution to solve this problem.