Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Cage Sarin
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Cage Sarin

The Chalk Module Can Be Used With The Console's String Interpolation In Node.js

By
Published in Comments (1)

If you use Node.js, you're probably familiar with the Chalk module. It's one of my favorite npm modules; and, is generally one of the first modules that I install when I'm playing around with Node.js. The Chalk module allows you to add styles to your terminal output. Which is awesome. But, the other day, I stumbled upon the fact that these Chalk styles can also be applied when using the console's string interpolation functionality. Which makes Chalk even moar awesome!

If you are unaware of how the console module works in Node.js, it has two basic features: it concatenates strings and it interpolates strings. Under the hood, these features are actually provided by the util.format() method; but, the console module writes the results of the util.format() call to a stream (the standard-out stream by default).

That said, I've historically only used Chalk with the string concatenation feature of the console module. But, it totally works with the string interpolation feature we well - with some caveats. To see this in action, take a look at the following demo:

// Require the core node modules.
var chalk = require( "chalk" );

// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //

// The approach that I normally use when nesting styles is to include a variable
// number of arguments in the root chalk() call. This allows the styles to build upon
// each other.
// --
// NOTE: Using the "+" in order to prevent a space before the ",".
process.stdout.write( "1: " );
console.log( chalk.red( "Hello", chalk.bold( "Kim" ) + ", I hope you are well." ) );

// The above works perfectly; but, it leaves the code with some readability problems.
// We can try to make the logic and formatting easier to read by using the "formatting"
// functionality of the Console (supplied under the hood by util.format()). Notice that
// in this version, we're using "%s" to interpolate original log message AND that we're
// using chalk in the interpolation value.
process.stdout.write( "2: " );
console.log( chalk.red( "Hello %s, I hope you are well." ), chalk.bold( "Kim" ) );

// Using the Console interpolation, however, doesn't work in all cases. It works for
// any case in which we are NOT OVERRIDING COLORS (foreground or background). Colors
// get some special treatment. But, any non-color modifiers can be used safely in the
// interpolation value.
process.stdout.write( "3: " );
console.log(
	chalk.red( "Hello %s, I %s you are %s." ),

	// Here, are keeping the same COLOR (background / foreground), but we're adding
	// style modifiers. This works! This is cool beans!
	chalk.bold( "Kim" ),
	chalk.underline( "rather hope" ),
	chalk.italic( "happy" )
);

// If we NEST THE STYLES directly, however, we can definitely override colors. This is
// because chalk is smart enough to know styles are being nested and will RE-OPEN the
// previous style when the nested style is closed.
process.stdout.write( "4: " );
console.log( chalk.red( "Hello", chalk.black( "Kim" ) + ", I think you are great!" ) );

// ... however, chalk can only be smart if it is doing the nesting directly. If we rely
// on the console interpolation to do the nesting, chalk doesn't see it, so IT CANNOT
// RE-OPEN THE STYLE. This leaves the remainder of the text with the DEFAULT colors.
// --
// CAUTION: THIS APPROACH DOES NOT WORK (for foreground or background conflicts).
process.stdout.write( "5: " );
console.log( chalk.red( "Hello %s, I think you are great!" ), chalk.black( "Kim" ) );

// Of course, if the outer console item has the DEFAULT background and foreground colors,
// then we can totally use color styles in the interpolation - since the reset just puts
// the style back into the default state.
process.stdout.write( "6: " );
console.log( "Hello %s, so great running into you!", chalk.red.bold( "Kim" ) );

// NOTE: We don't need to use variable arguments in order to get the nested styles to
// work properly. We can use string templates to get the same thing to work. This is
// because chalk is actually using PATTERN MATCHING to find the CLOSE pattern of any
// nested styles in order to replace them with an OPEN pattern of the parent style.
process.stdout.write( "7: " );
var value = chalk.black.bold.underline( "Kim" );
console.log( chalk.red( `Hello ${ value }, rock on with your bad self.` ) );

As you can see, I'm using the "%s" (String) interpolation placeholder when calling the console.log() method. Then, I'm using the 1...N arguments in order to provide nested Chalk styles.

When we run this code in Node.js, we get the following terminal output:

Using chalk with the console's interpolation functionality in Node.js.

As you can see, all of the above work, with the exception of number 5:

console.log( chalk.red( "Hello %s, I think you are great!" ), chalk.black( "Kim" ) );

It turns out that we can use Chalk with console string interpolation as long as the interpolated nested style does not change the foreground or background colors. When Chalk manages nested styles directly, it uses RegExp patterns to search for closing markers and then explicitly re-opens the parent style. With interpolation, however, Chalk never has a chance to do this, which means that the nested style's close marker leaves the remainder of the value with the default colors.

NOTE: This is OK if the parent style doesn't use colors. In that case, the reset of the nested style has no symptomatic affect upon the result.

I know this is such a small feature. But I love Chalk; and, being able to use it with the console's string interpolation makes it all the more powerful. And, if you didn't know that you could do this, hopefully you found this post as exciting as I did!

Want to use code from this post? Check out the license.

Reader Comments

15,902 Comments

@All,

Being able to use `template` interpolation doesn't work as expected with all styles. Just be sure to test the combinations that you use. For example, if we do this:

console.log( chalk.dim( `Alpha ${ chalk.bold( 'Beta' ) } Charlie` ) );

... the BOLD style bleeds out into "Charlie.". However, if we do this:

console.log( chalk.red( `Alpha ${ chalk.bold( 'Beta' ) } Charlie` ) );

... the BOLD style is only applied to "Beta." The difference is that the outer style is "dim" in the first example and "red" in the latter example. I believe that the latter has special treatment because "Red" is a color and not a text modifier? Just a guess.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel