Using clsx
or classnames
in React lately has become a trend with utility-first CSS frameworks like Tailwind CSS, where you have to write a lot of classes, and sometimes also with conditionals.
If you come from a framework like Vue you may saw that there you have this Class Binding functionality out of the box.
In this blog post I will show you how you can patch React library, so you don't have import clsx
everywhere where you need it, and to have this functionally out of the box also in React.
☢️ This is just for learning purposes. Use at your own risk
Create new React App
yarn create react-app implicit-clsx
cd implicit-clsx
Install clsx
yarn add clsx
Remove react
yarn remove react
Install react
under raw-react
name (More about NPM Aliases)
yarn add raw-react@npm:react
Create own React that will export from raw-react
my-react/index.js
module.exports = require('raw-react')
my-react/jsx-runtime.js (About JSX Runtime)
module.exports = require('raw-react/jsx-runtime')
my-react/jsx-dev-runtime.js (About JSX Runtime)
module.exports = require('raw-react/jsx-dev-runtime')
Install my-react
as react
package (More about NPM Aliases)
yarn add react@file:my-react
Patch JSX Runtime
Now let's patch JSX Runtime to check for className
. Here comes the hard work 😀
my-react/jsx-dev-runtime.js
module.exports = require('raw-react/jsx-dev-runtime')
const clsx = require('clsx').default
const jsxDEV = module.exports.jsxDEV
module.exports.jsxDEV = function() {
if (typeof arguments[0] === 'string' && arguments[1].className) {
arguments[1].className = clsx(arguments[1].className)
}
return jsxDEV.apply(undefined, arguments)
}
Now it's time to explain what gibberish I wrote here 🤣 I will explain some things only everything else I think it's clear
-
arguments
arguments is an Array-like object accessible inside functions that contains the values of the arguments passed to that function. -
apply
The apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object).
Signature of function jsxDEV
is type, props, key
. So arguments[0]
is type
and arguments[1]
is props
And we check if type is a string, because in react-dom
host elements can be only strings, we don't want to change for example className
on some function or class components.
And second we check if we have className
prop, we patch it with a clsx
call.
Last line in the function we just forward everything to the native jsxDEV
To have this work also on build, you will need to apply this patch also to
jsx
andjsxs
inmy-react/jsx-runtime.js
see repo link at the end
Reinstall my-react
as react
package to update
yarn add react@file:my-react
Change App.js
to see the changes
Replace line with a single className
as string
<div className="App">
To something that usually can be passed to clsx
<div className={["App1", "App1", { "App2": true }]}>
Start the app and let's check in browser
yarn start
Working example https://github.com/iamandrewluca/implicit-clsx
I played around with TypeScript version, but couldn't make it work because of types mismatch, needs more investigation.
And we are done! Thanks for reading my blog posts!
Cover Photo by Ash from Modern Afflatus on Unsplash
Top comments (1)
I'm happy to discover fine tuning like these are actually quite easy to implement.
Build-in
clsx
would be awesome!