DEV Community

Cover image for 🧶 Implicit CLSX in React
Andrew
Andrew

Posted on

🧶 Implicit CLSX in React

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
Enter fullscreen mode Exit fullscreen mode

Install clsx

yarn add clsx
Enter fullscreen mode Exit fullscreen mode

Remove react

yarn remove react
Enter fullscreen mode Exit fullscreen mode

Install react under raw-react name (More about NPM Aliases)

yarn add raw-react@npm:react
Enter fullscreen mode Exit fullscreen mode

Create own React that will export from raw-react

my-react/index.js

module.exports = require('raw-react')
Enter fullscreen mode Exit fullscreen mode

my-react/jsx-runtime.js (About JSX Runtime)

module.exports = require('raw-react/jsx-runtime')
Enter fullscreen mode Exit fullscreen mode

my-react/jsx-dev-runtime.js (About JSX Runtime)

module.exports = require('raw-react/jsx-dev-runtime')
Enter fullscreen mode Exit fullscreen mode

Install my-react as react package (More about NPM Aliases)

yarn add react@file:my-react
Enter fullscreen mode Exit fullscreen mode

Patch JSX Runtime

Now let's patch JSX Runtime to check for className. Here comes the hard work 😀

Image description

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)
}
Enter fullscreen mode Exit fullscreen mode

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 and jsxs in my-react/jsx-runtime.js see repo link at the end

Reinstall my-react as react package to update

yarn add react@file:my-react
Enter fullscreen mode Exit fullscreen mode

Change App.js to see the changes

Replace line with a single className as string

<div className="App">
Enter fullscreen mode Exit fullscreen mode

To something that usually can be passed to clsx

<div className={["App1", "App1", { "App2": true }]}>
Enter fullscreen mode Exit fullscreen mode

Start the app and let's check in browser

yarn start
Enter fullscreen mode Exit fullscreen mode

Image description

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

Discussion (0)