DEV Community

Cover image for Stop Importing clsx in React. The Luxury of "Pure" JSX.
Zsolt Tövis
Zsolt Tövis

Posted on

Stop Importing clsx in React. The Luxury of "Pure" JSX.

I was refactoring a component last night. It was a simple button, nothing fancy. But as I stared at the file, something bothered me. It wasn’t the logic. It wasn’t the styles.

It was the imports.

import { useState } from 'react';  
import clsx from 'clsx'; // <--- This one.  
import styles from './Button.module.css';
Enter fullscreen mode Exit fullscreen mode

We have become addicted to imports. We’ve accepted that to do something as fundamental as toggling a CSS class based on a condition, we need to pull in a utility library and wrap our strings in a function call.

Why?

Why do we treat className like a second-class citizen that only understands strings, forcing us to do the heavy lifting?

I realized that bare code is a luxury. The less I have to import, the less visual noise I have to filter out, and the more I can focus on what the component actually does.

So, I decided to fix it. I didn’t want a wrapper component. I didn’t want a complex build step. I wanted the React runtime itself to be smarter.

Meet clsx-react.

The Problem: The “Utility” Tax

We’ve all written this code thousands of times:

// The "Standard" Way  
import clsx from 'clsx';  

export const Card = ({ isActive, isDisabled, children }) => {  
  return (  
    <div  
      className={clsx(  
        'p-4 rounded-lg border',  
        isActive ? 'border-blue-500' : 'border-gray-200',  
        isDisabled && 'opacity-50 cursor-not-allowed'  
      )}  
    >  
      {children}  
    </div>  
  );  
};
Enter fullscreen mode Exit fullscreen mode

It works. It’s fine. But it’s noisy. You have to remember to import clsx. You have to remember the syntax. It breaks the visual flow of the JSX.

The Solution: Native Syntax

What if className just... understood? What if it accepted arrays and objects natively, just like clsx does, but without the import?

With clsx-react, that same component looks like this:

// The "Pure" Way  
// No import. No wrapper function. Just data.  
export const Card = ({ isActive, isDisabled, children }) => {  
  return (  
    <div  
      className={[  
        'p-4 rounded-lg border',  
        isActive ? 'border-blue-500' : 'border-gray-200',  
        { 'opacity-50 cursor-not-allowed': isDisabled }  
      ]}  
    >  
      {children}  
    </div>  
  );  
};
Enter fullscreen mode Exit fullscreen mode

Look at that. It’s clean. It reads like a configuration, not a function call. The className prop is no longer a dumb string receiver; it’s an intelligent API.

How It Works (No Magic, Just Standards)

This isn’t a hack. It leverages the standard jsxImportSource capability of modern bundlers (Vite, TypeScript, etc.).

Instead of React’s default runtime creating the element, clsx-react sits in the middle. It intercepts the props, sees an array or object in className, resolves it (using the tiny clsx logic under the hood), and passes a clean string to React.

It happens at the runtime level. Your component code remains pure.

How to Get It

It is zero-dependency (other than the runtime itself) and super lightweight.

Install it:

npm install clsx-react
Enter fullscreen mode Exit fullscreen mode

Tell your compiler to use it:

// tsconfig.json / jsconfig.json  
{  
  "compilerOptions": {  
    "jsx": "react-jsx",  
    "jsxImportSource": "clsx-react"  
  }  
}  

// or Vite config  
export default defineConfig({  
  plugins: [  
    react({ jsxImportSource: 'clsx-react' })  
  ]  
})  

// or Vite config / esbuild  
export default defineConfig({  
  plugins: [react()],  
  esbuild: {  
    jsxImportSource: 'clsx-react',  
  },  
});  

// or Babel / Webpack  
{  
  "presets": [  
    [  
      "@babel/preset-react",  
      {  
        "runtime": "automatic",  
        "importSource": "clsx-react"  
      }  
    ]  
  ]  
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

In a modern developer stack, we often mistake adding tools for adding value. Sometimes, real value comes from removing friction.

By moving the class logic into the runtime, we remove one import from every single file. We remove the visual clutter of function calls in our JSX. We get back to the luxury of writing bare, simple code.

Give it a try. Once you stop importing clsx, you won’t want to go back.

GitHub Repo: toviszsolt/clsx-react

Top comments (1)

Collapse
 
toviszsolt profile image
Zsolt Tövis

I built this because I'm lazy and I love clean code. Let me know what you think!"