loading...
Cover image for You do not need to use the classnames package

You do not need to use the classnames package

nickytonline profile image Nick Taylor (he/him) Originally published at iamdeveloper.com on ・2 min read

Photo by David Rotimi on Unsplash

Do not get me wrong, the classnames package is really handy. It is also quite popular with just over 3.5 million downloads per week as of the date of this blog post. Most React based projects I have worked on use it.

If you are not familiar with the classnames package, it allows you to build a set of CSS classes based on some conditionals. Straight from there documentation:

classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'

Note: true and false are used to simplify the example, but normally these would be conditional variables, methods or functions.

Having said that, JavaScript has come a long way and there are features in the language that allow us to do pretty much the same thing, specifically template strings or as it is also called, template literals.

If you are not familiar with template strings, you can build a string with variables mixed in. Let us look at the previous examples, but this time with template strings.

`foo bar` // => 'foo bar', not that exciting
`foo ${ true ? 'bar': '' }`; // => 'foo bar'
`${true ? 'foo-bar': '' }`; // => 'foo-bar'
`${ false ? 'foo-bar' : ''}` // => ''
`${ true? 'foo': '' }, { true ? 'bar': '' }`; // => 'foo bar'
`${ true ? 'foo' : ''} ${ true? 'bar' : '' }`; // => 'foo bar'

These are trivial examples, but it is just to show you that you can do pretty much the same thing with template literals. If you want to see this in action, here is an example from my site's source:

GitHub logo nickytonline / old_www.iamdeveloper.com

Old source code for my web site iamdeveloper.com

iamdeveloper.com

Netlify Status

Dependabot Badge

Hey there, I'm Nick and this is my site's source code. This site started off as a clone of the Netlify CMS Gatsby Starter (check it out!). Since then, I've tweaked it a lot and converted the codebase to TypeScript.

Feel free to peruse the code and/or fork it. 😉

Thanks to all the wonderful projects that made it possible to build this blog.

I wrote about automating my deployments for those interested in my post Update Dependencies with Dependabot, Cypress and Netlify.

To get up and running:

  • clone the repository by running git clone git@github.com:nickytonline/www.iamdeveloper.com.git or git clone https://github.com/nickytonline/www.iamdeveloper.com.git
  • run npm install
  • run npm run develop to get up and running with the Gatsby development server.
  • Since the project uses Babel and not TypeScript as the compiler, a separate process is…
...
<nav
   className={`navbar is-transparent ${styles.navbar}`}
   role="navigation"
   aria-label="main-navigation"
   data-cy="nav-bar"
>
...

https://github.com/nickytonline/www.iamdeveloper.com/blob/master/src/components/Navbar.tsx#L51

This is not mind blowing code, but just another way to do it.

Happy coding!

Posted on by:

nickytonline profile

Nick Taylor (he/him)

@nickytonline

Senior software engineer at DEV/Forem. Caught the live coding bug on Twitch at livecoding.ca

Discussion

pic
Editor guide
 
`${ false ? '' : 'foo-bar'}` // => ''

this should return 'foo-bar' instead of ''.

For simple use cases classnames is definitely an overkill, but I would say if you have more than 2 conditions in the same template literal it becomes ugly really quickly.

Also there is a smaller package for the same purpose called clsx.

 

Thanks, correction made. Yes, agreed that it could get unwieldy, just showing another way of doing things is all. 😉

 

I remember ditching classnames long ago. At some point we've decided that vendor for joining strings is an overkill. I think we've used something like this (pseudocode):

const cls = (input: string|boolean[]): string => input.filter((cond: string | boolean) => typeof cond === "string").join(", ");

<div className={cls(["one", isTrue ? "two" : "three", isAnything && "four"]) />

And it worked perfectly. You could drop the filter element, but then you'll get false as one of the classes from time to time.

 

Agreed, I think classNames is a bit of overkill for what is essentially concatenation of strings. However template literals can get hectic and hard to reason about very quickly. What I normally do is:

const componentClassName = [
  'some-base-class',
  someBooleanCondition && 'a-class-in-here',
  someOtherBoolean && 'another-class',
]
  .filter(Boolean)
  .join(' ')

Written on a mobile so forgive any typos!

 

I've been using template strings to build classes for a while and every time I come up against an existing project which uses classNames I scratch my head trying to understand why this exist. Thanks for validating this view.

 

I had written a very similar blogpost about 2 years ago. Eventually after using template literals in major projects, (some of them currently in production on zomato), I found a number of pitfalls and redundancies with this approach. This led me to developing a safer and easier-to-use template literals based classNames alternative called classd. Recently I wrote a blogpost on opensourcing classd describing the usecases. classd also provides a classNames like function (classdFn) which is intended to be a drop-in replacement for classNames. It supports more types than POJOs (like maps, sets and iterables) and in majority of cases it's more performant than classNames. Do give it a try if you are interested in using template literals for composing classes.