Giter VIP home page Giter VIP logo

clsx's Introduction

clsx CI codecov licenses

A tiny (239B) utility for constructing className strings conditionally.
Also serves as a faster & smaller drop-in replacement for the classnames module.

This module is available in three formats:

  • ES Module: dist/clsx.mjs
  • CommonJS: dist/clsx.js
  • UMD: dist/clsx.min.js

Install

$ npm install --save clsx

Usage

import clsx from 'clsx';
// or
import { clsx } from 'clsx';

// Strings (variadic)
clsx('foo', true && 'bar', 'baz');
//=> 'foo bar baz'

// Objects
clsx({ foo:true, bar:false, baz:isTrue() });
//=> 'foo baz'

// Objects (variadic)
clsx({ foo:true }, { bar:false }, null, { '--foobar':'hello' });
//=> 'foo --foobar'

// Arrays
clsx(['foo', 0, false, 'bar']);
//=> 'foo bar'

// Arrays (variadic)
clsx(['foo'], ['', 0, false, 'bar'], [['baz', [['hello'], 'there']]]);
//=> 'foo bar baz hello there'

// Kitchen sink (with nesting)
clsx('foo', [1 && 'bar', { baz:false, bat:null }, ['hello', ['world']]], 'cya');
//=> 'foo bar hello world cya'

API

clsx(...input)

Returns: String

input

Type: Mixed

The clsx function can take any number of arguments, each of which can be an Object, Array, Boolean, or String.

Important: Any falsey values are discarded!
Standalone Boolean values are discarded as well.

clsx(true, false, '', null, undefined, 0, NaN);
//=> ''

Modes

There are multiple "versions" of clsx available, which allows you to bring only the functionality you need!

clsx

Size (gzip): 239 bytes
Availability: CommonJS, ES Module, UMD

The default clsx module; see API for info.

import { clsx } from 'clsx';
// or
import clsx from 'clsx';

clsx/lite

Size (gzip): 140 bytes
Availability: CommonJS, ES Module
CAUTION: Accepts ONLY string arguments!

Ideal for applications that only use the string-builder pattern.

Any non-string arguments are ignored!

import { clsx } from 'clsx/lite';
// or
import clsx from 'clsx/lite';

// string
clsx('hello', true && 'foo', false && 'bar');
// => "hello foo"

// NOTE: Any non-string input(s) ignored
clsx({ foo: true });
//=> ""

Benchmarks

For snapshots of cross-browser results, check out the bench directory~!

Support

All versions of Node.js are supported.

All browsers that support Array.isArray are supported (IE9+).

Note: For IE8 support and older, please install [email protected] and beware of #17.

Tailwind Support

Here some additional (optional) steps to enable classes autocompletion using clsx with Tailwind CSS.

Visual Studio Code
  1. Install the "Tailwind CSS IntelliSense" Visual Studio Code extension

  2. Add the following to your settings.json:

 {
  "tailwindCSS.experimental.classRegex": [
    ["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
  ]
 }

You may find the clsx/lite module useful within Tailwind contexts. This is especially true if/when your application only composes classes in this pattern:

clsx('text-base', props.active && 'text-primary', props.className);

Related

  • obj-str - A smaller (96B) and similiar utility that only works with Objects.

License

MIT © Luke Edwards

clsx's People

Contributors

acusti avatar andipaetzold avatar chulanovskyi avatar danikaze avatar edgarlr avatar gingerrific avatar jalalazimi avatar kevinlowe0x3f7 avatar lukeed avatar maraisr avatar remcohaszing avatar rohmanhm avatar tariqporter avatar viliamkopecky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clsx's Issues

Odd Webpack issue dropping clsx

This is quite hard to debug as it's part of a Storybook build process. clsx works fine when in development mode - however when we run build-storybook it seems to drop the module.

Cannot read property 'default' of undefined
TypeError: Cannot read property 'default' of undefined
    at http://127.0.0.1:8080/main.4e9c45778b78ebc1cf6a.bundle.js:1:39326
    at Ih (http://127.0.0.1:8080/vendors~main.4e9c45778b78ebc1cf6a.bundle.js:85:59571)

Piece of compiled JS from webpack:

composedClassName=(0,_clsx.default)("a-button","a-button--".concat(variant),(_clsx={"a-button--transparent":isTransparent,"a-button--form":isForm&&(isPrimary||isSecondary),"a-button--full-width":isFullWidth,"a-button--input":isInput},(0,_defineProperty2.default)(_clsx,"a-button--".concat(icon),icon&&!isTransparent&&!isFullWidth),(0,_defineProperty2.default)(_clsx,"is-disabled",isDisabled),_clsx),className);

I've narrowed it down to this:

const composedClassName = clsx(
      'a-button',
      `a-button--${variant}`,
      {
        [`a-button--${icon}`]: icon && !isTransparent && !isFullWidth,
        'a-button--transparent': isTransparent,
        'a-button--form': isForm && (isPrimary || isSecondary),
        'a-button--full-width': isFullWidth,
        'a-button--input': isInput,
        'is-disabled': isDisabled,
      },
      className
    );

Which seems to happen when combining the [key] method with regular strings... so, while the above fails, this succeeds on placing it in its own object.

const composedClassName = clsx(
      'a-button',
      `a-button--${variant}`,
      {
        [`a-button--${icon}`]: icon && !isTransparent && !isFullWidth,
      },
      {
        'a-button--transparent': isTransparent,
        'a-button--form': isForm && (isPrimary || isSecondary),
        'a-button--full-width': isFullWidth,
        'a-button--input': isInput,
        'is-disabled': isDisabled,
      },
      className
    );

I don't suppose you have any idea of why this might be happening?

Babel config looks like:

const useModules = process.env.BUILD_FORMAT === 'esm';

module.exports = {
  env: {
    test: {
      presets: ['@babel/preset-react', '@babel/preset-env'],
    },
    development: {
      presets: ['@babel/preset-react', '@babel/preset-env'],
    },
    production: {
      presets: [
        '@babel/preset-react',
        [
          '@babel/preset-env',
          {
            modules: useModules ? false : 'commonjs',
          },
        ],
      ],
      plugins: [
        'babel-plugin-optimize-clsx',
        '@babel/plugin-transform-react-constant-elements',
        [
          '@babel/plugin-transform-runtime',
          {
            corejs: false,
            helpers: true,
            regenerator: true,
            useESModules: useModules,
          },
        ],
      ],
    },
  },
};

I have tried removing the babel-plugin-optimize-clsx plugin, but it makes no difference. Not that it makes any difference, but classnames package works as expected - so I'm only guessing it's something to do with clsx itself.

Why should i use this?

Hi, maybe this is not the normal message you expect in Issues but there is no tab for discussions in this repository.

My question is:
Why should I use CLSX instead of just applying conditional classes with template strings? Is it because the syntax is neater or it has better performance?

Goodbye :)

UPDATE: Yap, has better performance. I just saw the benchmarks, sorry.

What is the purpose of this?

Hi all, just using a starter project and some of the classes are added like this:

        clsx(
          'absolute right-0 flex h-full items-center', //
          'md:mr-4',
        )

Can anyone explain the purpose of this? Why not just do:

        clsx(
          'absolute right-0 flex h-full items-center md:mr-4',
        )

Will this library still be maintained?

I saw some improvements proposed by the community as pull request, that weren't merged for about 2 years.

Also this project "builder" is using an outdated terser version. Maybe with new terser version, dist file can be even smaller, I don't know. But is something to take a look at

Module format error on import with vitest

Hey!
I get the following error when running a component test that imports clsx in vitest:

(node:57821) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
 ❯ src/Route.test.ts (0)

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Suites 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL  src/Route.test.ts [ src/Route.test.ts ]
SyntaxError: Unexpected token 'export'
 ❯ Object.compileFunction node:vm:360:18

Module /node_modules/clsx/dist/clsx.m.js:1 seems to be an ES Module but shipped in a CommonJS package. You might want to create an issue to the package "clsx" asking them to ship the file in .mjs extension or add "type": "module" in their package.json.

As a temporary workaround you can try to inline the package by updating your config:

// vitest.config.js
export default {
  test: {
    deps: {
      inline: [
        "clsx"
      ]
    }
  }
}

Seems like renaming clsx.m.js to clsx.mjs in the dist folder does the trick.

Snapshot test failed with auto-generated classname

Hi @lukeed ,

I've had a problem with snapshot testing (Jest & @testing-library/react).
When I run test twice with toMatchSnapshot(), the new snapshot was be different from the old one because of auto-generated classname. Example as:

data-tableid="022204747978025785"

data-tableid="043447615246065774"

I have researched a lot but haven't found how to pass this issue.
Do you have any suggestions for me in this case?

Thanks,

TypeScript support

First of all love this lib! Neat. 💯

TypeScript people had @types/classnames - with this lib being a drop in replacement, should we fork that typings, or simply include it in this repo.

declare module 'clsx' {
	type ClassValue = string | number | ClassDictionary | ClassArray | undefined | null | boolean;

	interface ClassDictionary {
		[ id: string ]: any;
	}

	interface ClassArray extends Array<ClassValue> {
	}

	type ClassNamesFn = (...classes: ClassValue[]) => string;

	type ClassNamesExport = ClassNamesFn & { default: ClassNamesFn };

	const classNames: ClassNamesExport;

	export = classNames;
}

sourced from: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/classnames/index.d.ts

Not sure if you wan't to include this in your project as a index.d.ts in the root.

TS issue importing from clsx/lite

Excited to try out the new lite approach:

import { clsx } from 'clsx/lite';

But I'm running into a TypeScript import error:

CleanShot 2023-12-30 at 13 50 57@2x

Is there something I'm doing wrong? Installed clsx 2.1.0 via pnpm 8.12.1

No classname will be generated from objects that have the "push" member set to true

In the following case all the classnames defined in the object are missing from the result:

clsx('stack', { pop: true, push: true }); // => 'stack'

This is caused by the if (!!mix.push) { check for Array, this makes clsx so fast, but I think it should be replaced with Array.isArray() or at least add this case to readme to warn people that get into this issue.

Can you please fix this issue ?

import clsx from "clsx";
clsx('text-sm text-bold', 'text-red' , 'text-semibold text-lg', 'text-blue');

This is not going to work fine. only one text- will work when we run this and this will also not override classes.

Undefined class name gets printed

I have this code

import styles from './styles.css';

clsx({[styles.active]: isActive}); // returns "undefined" when `styles.active` is undefined

Live demo

Ideally this should work the same way as this code: clsx(isActive && styles.active)

Congrats!

clsx just overtook classnames, the package clsx was designed be a drop-in replacement for.

image

To celebrate and as a token of appreciation (I'm using your package virtually everywhere) I'm also sending a small donation alongside this message 🩷

Classes are not being over written

I have this scss:

.primary {
    @include button;
    color: #FFF;
    background-color: #FF5A5A;
    &:hover {
        background-color: #FF2E40;
    }
}
.outlined {
    @include button;
    border: 1px solid #2156FA;
    color: #2156FA;
    background-color: #FFF;
    &:hover {
        color: #FFF;
        background-color: #2156FA;
    }
}

and I try to use this with clsx:

className={clsx(
                type === 'outlined' && cls.outlined,
                color === 'primary' && cls.primary,
                color === 'secondary' && cls.secondary,
            )}
... other place ...
<button color="primary" type="outlined"/>

it generates the outlined class and the primary class, but outlined always wins over primary for background-color. (yes I need to rework the css to use variables, I know)

I would assume that if primary comes after outline, primary should win, but it doesn't. Any combination with outline, and it always wins
Maybe I'm using it wrong? please let me know.

Thank you.

I am writing this just to say thank you.
Really thank you for your work and for what you teach the world.
❤️

Is it possible to enable `sourcemap` ?

Hi,

Thanks for creating this awesome tool. I was wondering if you could add sourcemap for the minified bundles.

Reasons :
The original code of clsx is not included in the project that uses clsx
image

Avoid having to use esModuleInterop

ES6 modules are supposed to be an object, however this package exports a function by default.
To make it work in TypeScript it requires this option set to true, which means extra code in the build... and would be nice not requiring it.

now:

import clsx from 'clsx';

after:

import { clsx } from 'clsx';

Since this would be a breaking change, I would suggest applying this fix in a way that the old version is also supported -which requires a bit of dirty code- and then apply the clean version in a major version up release :)

// the dirty hack would look like this (types also required)
module.exports.clsx = module.exports;

// so you actually can do both in this version preserving compatibility
import clsx from 'clsx';
import { clsx } from 'clsx';

next version (let's say a major version up) could just export the function like this:

function clsx () {
	var i=0, tmp, x, str='';
	while (i < arguments.length) {
		if (tmp = arguments[i++]) {
			if (x = toVal(tmp)) {
				str && (str += ' ');
				str += x
			}
		}
	}
	return str;
}

module.exports = { clsx };

With this migration we are achieving two things:

  1. Following ES6 standards
  2. Requiring less code when compiling in typescript since esModuleInterop wouldn't be required anymore

strings should be trimmed?

If my string has space in it - I would like to be trimmed all incomming strings.
It does not make sense to have them here

image

syntax question

Hi,

I'm confusing, why this line is working :

        clsx(
          true && 'c2'
        )

and this is not :

        clsx(
          'c2' && true
        )

Thanks for help

What's the difference with classcat

Hey,

What's the difference with classcat package? It was also released as a faster/smaller/esm ready version of classnames a year ago.

Why another package with same purpose? It even has a benchmark where clsx seems to be many times slower. 🤔

Thanks

TypeScript Issue

"typescript": "5.0.4",
"clsx": "1.2.1"

Hey there, I need clarification on what is happening right now. I have the following piece of code:

import clsx from 'clsx';
import Link from 'next/link';

type ConnectWithUsButtonProps = { className?: string };
// This one fails as well.
// type ConnectWithUsButtonProps = { className: string | undefined };

export function ConnectWithUsButton(props: ConnectWithUsButtonProps) {
  return (
    <Link
      href="/"
      className={clsx(
        'flex-shrink-0 bg-yellow-300 hover:bg-yellow-400 font-bold text-black rounded-full py-4 px-6 shadow-lg',
        // TODO: remove ! from className
        props.className!
      )}
    >
      Connect with us
    </Link>
  );
}

I am getting the following issue:

Argument type string | undefined is not assignable to parameter type ClassValue

Screen Shot 2023-05-16 at 11 47 02 PM

I am not sure why I must use ! now when it used to work just fine before.

I do not get why this is happening. Everything seems just fine.

I really appreciate any help you can provide.

Could default exported function perform a tiny bit better?

export default function () {
- 	var i=0, tmp, x, str='';
+ 	var tmp, x, len=arguments.length, str='', i=0;
-	while (i < arguments.length) {
+	while (i < len) {
		if (tmp = arguments[i++]) {
			if (x = toVal(tmp)) {
				str && (str += ' ');
				str += x
			}
		}
	}
	return str;
}

Benchmark on only string concatenation shows an increase of about 1,000,000 operation/sec.
Note: I got benchmark results only in node, in a really inaccurate way. but theoretically, it must increase the performance.

CSS Variables not working in clsx

Hi There, I'm using clsx and tailwindcss for styling my nextjs project, but when I set css variables in clsx like this

<figure className={clsx(baseImgWrapperClassName, "flex items-start")}>
  <Image
    src={People1}
    alt="Hero Animation image 1"
    className={clsx(
      {
        "--max-height": "80%"
      },
      "pointer-events-none w-full rounded-[18px] object-cover lg:rounded-[36px]",
      "about-us-hero-animation-top translate-y-[25%] bg-[#EDDDDD]"
    )}
  />
</figure>

and css:

@keyframes about-us-hero-animation-top {
  0% {
    opacity: 0;
    height: 0;
  }
  100% {
    opacity: 1;
    height: var(--max-height, 100%);
  }
}

why is --max-height variable unreadable in chrome devtools? it should be displayed as attribute style right? see devtools screenshot here

Btw I tried this method from the example of usage in the clsx documentation (https://github.com/lukeed/clsx#usage)

// Objects (variadic)
clsx({ foo:true }, { bar:false }, null, { '--foobar':'hello' });
//=> 'foo --foobar'

"This expression is not callable." error when using TypeScript with moduleResolution "node16" or "nodenext"

When using TypeScript with moduleResolution "node16" or "nodenext", importing clsx and using it:

import clsx from 'clsx';

clsx('a', 'b', 'c');

produces the following error:

src/index.tsx(3,0): error TS2349: This expression is not callable.
  Type 'typeof import("/node_modules/clsx/clsx")' has no call signatures.

It appears the problem is with the way typings are shipped with this library. Please read:
microsoft/TypeScript#49189 (comment)

The thread also mentions the same problem with classnames package, but it appears that they have dealt with it since then.

Performance question

Hi.
Is it a good practice to use clsx even if I have only one conditionnaly class ?

Exemple

Insteaf of this :

const class = (condition) ? 'Myclass' : undefined
<div className={class}/>

I prefer this approach:

<div className={clsx(
{
'Myclass': condition
}
)}/>

But in term of performance, is there any impact ?

Thanks for replay

Dynamic Duplicate Class Names Get Removed

Hello,

I've ran into a scenario where neither condition supplies a className in a ClassDictionary.

{
	[`text-${props.dynamicColor}`]: !!props.dynamicColor,
	'text-blue': !props.dynamicColor,
}

If props.dynamicColor is blue, then neither class gets applied. It would be ideal if the !!props.dynamicColor class would get applied even if it's blue. Otherwise one could potentially end up with a bug. I've created a codepen to demonstrate. Let me know if I can answer any questions or provide anymore context.

Supporting unwanted undefined in object

Is there any plans for supporting this use case?

clsx({undefined: false});

Will return undefined which is unwanted.

Im using clsx in a React.js solution with CSS Modules like this:

 <section
      className={clsx(
        styles.test,
        { [styles[someOtherThing!]]: !!someOtherThing }
      )}
    >

In this case someOtherThing can be undefined or null and since i am doing !!someOtherThing then clsx should never return the "undefined" value.

Multiple issues with ES module

module is obsolete

package.json specifies:

"module": "dist/clsx.m.js",
"main": "dist/clsx.js",

The proper way is:

"type": "module",
"main": "dist/clsx.cjs",   // or .js if "type" is unspecified
"exports": {
  "import": "./dist/clsx.js",   // no need for .mjs if "type" is "module" (recommended)
  "require": "./dist/clsx.cjs"   // or .js if "type" is unspecified
},

Related issue: lukeed/bundt#7

.m.js extension should be .mjs

https://nodejs.org/docs/latest-v16.x/api/packages.html#determining-module-system

(.mjs is only necessary if "type": "module" is not specified)

Syntax import { clsx } from 'clsx' does not work in an app transpiled to CJS

clsx is undefined

TypeError: (0 , clsx__WEBPACK_IMPORTED_MODULE_0__.clsx) is not a function

Last line from dist/clsx.js is exports.clsx = clsx and exports comes from nowhere, I don't think this can work. Caused by https://github.com/lukeed/bundt I guess.

Solution I've found for import { clsx } from 'clsx' to work in an app transpiled to CJS

With webpack, create an alias clsx: 'clsx/dist/clsx.m.js', example with Next.js webpack config from next.config.js:

  // ...

  webpack: (config, options) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      clsx: 'clsx/dist/clsx.m.js'
    };

   // ...

This will force the import of dist/clsx.m.js instead of dist/clsx.js. (You will need to transpile node_modules/clsx with next-transpile-modules in the case of Next.js).





Yep, ES Module is a real challenge and a big mess :-/

Is There A Bug in Microsoft Edge that Impacts clsx?

MS Edge seems like it may have a bug in it that impacts clsx.

Edge displays a 100% repeatable bug on my web app. This bug doesn’t show up in Chrome, Firefox, or Safari. The stack trace doesn’t touch anyplace in my own Javascript code. The trace claims the bug in is in clsx, on a call to a function that clsx doesn't call. The bug halts execution of my code.

Here's the stack trace:

Screenshot 2023-08-10 at 10 27 26 AM

The stack trace claims the bug is on a call to the ApolloGraphQL function storeWriter.writeToStore located in clsx at line 1, position 374. But of course clsx doesn't interact with ApolloGraphQL at all.

The bug happens when my app runs an ApolloGraphQL function, but the stack trace (and it goes on about 5 times as long as the part of it shown in the screen shot) never points to a line in my own code.

So it seems like this may be a bug in MS Edge.

I'm just posting this here in case anyone has any thoughts or suggestions. I posted about this on the ApolloGraphQL community board too.

I'm trying to avoid taking hours to see if I can make a tiny project that demos the glitch, but that may be the only next move.

Add comparison with Classnames utility

https://github.com/JedWatson/classnames

There's a well established library which seems to do what this library does, is older and has more installations. To the casual observer it's not clear what's different between this library and that library. I've already seen people adding clsx to apps when classnames is already present probably because they (1) didn't know the prior exists and (2) like the terse syntax.

Please update your README to provide a comparison of the two tools and link to the other one and send them an issue to backlink to you. Otherwise we're looking at another husky situation down the line due to devs not realizing what this library is and isn't. Thanks.

Add documentation on usage with Tailwind CSS

I've been a user of this library for a long time and took me some time to figure out how to enable Tailwind IntelliSense inside clsx functions. I think could be helpful to add documentation on how to enable it to the README.

I'll open a PR

Combine variables without spaces

Hi! Is there a way to have conditional variables add onto the class string without spaces? If there is a way, I'm not understanding implementation from documentation.
Want to update the state, but cannot do this if a spaces exists. Current expected behavior:

<div className={ clsx( 'foo barr example_',
      {
        'zoo': !place,
        [place as string]: place
      }
    )}>
// 'foo barr example_ zoo'

Desired behavior:

<div className={ clsx( 'foo barr', 'example_' +
      {
        'zoo': !place,
        [place as string]: place
      }
    )}>
desired output // 'foo barr example_zoo'
actual output // 'foo barr example_[Object Object]'

I can have a crack at contributing if something like this doesn't exist! Thanks for any info or for a work around.

Tailwind regex flags unreachable paths

Hi Luke 👋

Please excuse my lack of regex knowledge. The Tailwind set-up instruction currently flags items in nested ternaries as conflicting, even though the logic paths are unique. Here's an example where a different size prop results in different class strings resulting in different sized text:

image

I'm guessing this is something that any amount of regex-fu isn't going to solve as it's runtime logic.

We chose to use nested ternaries because the string concat is much faster than array/object notation. It also means conditionals stop being checked as soon as the first passing condition is reached.

Would love to hear your thoughts 😄

Thanks,

Will.

Conditional keys that return undefined show up as "undefined" as a result

Hello!

We use Material UI to handle classNames within the solution and there are instances where we rely on Components passing down certain classes to child components and those child components use clsx to conditionally showcase certain styling, however, those instances are not always defined and when we do not handle undefined ourselves, it results in "undefined" as a class within the element. For example:

    const classes = {
      apple: undefined,
    }
    
    clsx({ [classes.apple]: true }) // returns the string "undefined"
    clsx({ [classes.banana]: true }) // returns the string "undefined"

My expectation would be that clsx checks for undefined in the object keys and when undefined exists, don't include that as part of the result.

Update benchmarks

classnames recently had some performance optimizing changes, it might therefore be a good idea to update the benchmarks with some more recent versions. From my testing on Node.js (haven't tested browsers) both classcat and classnames have seen significant performance boosts:

# Strings
  classcat*    x 14,797,704 ops/sec ±0.34% (97 runs sampled)
  classnames   x 16,512,233 ops/sec ±0.28% (99 runs sampled)
  clsx (prev)  x 15,628,285 ops/sec ±0.54% (95 runs sampled)
  clsx         x 17,359,926 ops/sec ±0.49% (99 runs sampled)

# Objects
  classcat*    x 13,320,894 ops/sec ±0.62% (93 runs sampled)
  classnames   x 13,160,650 ops/sec ±0.17% (96 runs sampled)
  clsx (prev)  x 10,128,094 ops/sec ±0.26% (94 runs sampled)
  clsx         x 12,825,083 ops/sec ±0.32% (98 runs sampled)

# Arrays
  classcat*    x 12,363,081 ops/sec ±0.46% (95 runs sampled)
  classnames   x 10,704,461 ops/sec ±0.33% (93 runs sampled)
  clsx (prev)  x 11,650,643 ops/sec ±0.20% (99 runs sampled)
  clsx         x 12,396,335 ops/sec ±0.19% (97 runs sampled)

# Nested Arrays
  classcat*    x 9,529,380 ops/sec ±1.14% (96 runs sampled)
  classnames   x 6,569,058 ops/sec ±0.26% (100 runs sampled)
  clsx (prev)  x 8,874,776 ops/sec ±0.47% (96 runs sampled)
  clsx         x 8,971,678 ops/sec ±0.40% (98 runs sampled)

# Nested Arrays w/ Objects
  classcat*    x 9,831,044 ops/sec ±0.56% (91 runs sampled)
  classnames   x 9,347,581 ops/sec ±0.34% (94 runs sampled)
  clsx (prev)  x 8,700,574 ops/sec ±0.32% (99 runs sampled)
  clsx         x 10,073,516 ops/sec ±0.58% (97 runs sampled)

# Mixed
  classcat*    x 10,498,507 ops/sec ±0.56% (92 runs sampled)
  classnames   x 10,229,073 ops/sec ±0.52% (97 runs sampled)
  clsx (prev)  x 9,538,485 ops/sec ±0.48% (97 runs sampled)
  clsx         x 10,647,664 ops/sec ±1.88% (95 runs sampled)

# Mixed (Bad Data)
  classcat*    x 2,449,938 ops/sec ±0.28% (97 runs sampled)
  classnames   x 2,780,279 ops/sec ±0.25% (97 runs sampled)
  clsx (prev)  x 2,815,139 ops/sec ±0.30% (98 runs sampled)
  clsx         x 3,080,118 ops/sec ±0.27% (98 runs sampled)

Add support for objects with `toString` methods

Hi there, I'm using a css-in-js library (stitches) which returns class names as objects with custom toString methods. This requires calling toString before passing the "class names" to clsx- otherwise clsx assumes the object is a config/mapping object.

// Current usage
const pink = css({ color: "pink" });
clsx(pink().toString())

I currently have a patch package which adds an additional check to the toVal function, checking to see if a mix object has it's own toString method and calls/returns that if so.

// General idea
if (mix.hasOwnProperty("toString")) {
  str += mix.toString();
}

I noticed that classnames has a condition for this (original PR). React also calls toString if you pass an object to className. I'm wondering if it might be useful to include this behavior in clsx? I can contribute a PR if so.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.