Skip to content

Add `clsx()` automatically to `className` in `React` and support `Typescript`.

License

Notifications You must be signed in to change notification settings

zjxxxxxxxxx/babel-plugin-clsx

Repository files navigation

babel-plugin-clsx npm

CI GitHub

Automatically add clsx for className in React and have the same fun without importing and writing it.

It is important to note that this library supports the use of Typescript projects. No other library of its kind has been found to do this.

Before doing so, make sure that clsx is installed or another available className handler exists for your project.

Install

npm

npm i --save-dev babel-plugin-clsx

yarn

yarn add --save-dev babel-plugin-clsx

pnpm

pnpm add --save-dev babel-plugin-clsx

Usage

Add the babel configuration

{
  "plugins": ["clsx"]
}

Your code

<div className={['c1', 'c2']} />;
<div className={{ c1: true, c2: true }} />;

After compilation

import _clsx from 'clsx';
<div className={_clsx('c1', 'c2')} />;
<div className={_clsx({ c1: true, c2: true })} />;

Options

options.[ static | strict | importSource | importName ]

interface Options {
  /**
   * @default true
   */
  static?: boolean;
  /**
   * @default true
   */
  strict?: boolean;
  /**
   * @default 'clsx'
   */
  importSource?: string;
  /**
   * @default 'default'
   */
  importName?: string;
}

options.static

By default, static mode is enabled, in which only array and object are converted, effectively avoiding duplicate processing of className. Of course, although it is not recommended to do so, you can still turn off this option, and after that, it will be up to you to handle or ignore unnecessary transformations.

Add the babel configuration

{
  "plugins": [
    [
      "clsx",
      {
        "static": false
      }
    ]
  ]
}

Your code

const className = ['c1', 'c2'];
<div className={className} />;

After compilation

import _clsx from 'clsx';
const className = ['c1', 'c2'];
<div className={_clsx(className)} />;

In an existing project, there may be a lot of code like this, and if you turn off static mode, there will be a lot of duplication.

Your code

import classNames from 'clsx';

// πŸ‘Ž This will repeat the process
const className = classNames('c1', 'c2');
<div className={className} />;

// πŸ‘ This does not repeat the process
<div className={classNames('c1', 'c2')} />;

After compilation

import _clsx from 'clsx';
import classNames from 'clsx';

// πŸ‘Ž This will repeat the process
const className = classNames('c1', 'c2');
<div className={_clsx(className)} />;

// πŸ‘ This does not repeat the process
<div className={classNames('c1', 'c2')} />;

options.strict

Strict mode is turned on by default, and you can turn it off if you want to add clsx to any attribute suffixed by className.

Add the babel configuration

{
  "plugins": [
    [
      "clsx",
      {
        "strict": false
      }
    ]
  ]
}

Your code

<Component
  className={['c1', 'c2']}
  headerClassName={['c1', 'c2']}
  footerClassName={['c1', 'c2']}
/>

After compilation

import _clsx from 'clsx';
<Component
  className={_clsx('c1', 'c2')}
  headerClassName={_clsx('c1', 'c2')}
  footerClassName={_clsx('c1', 'c2')}
/>;

options.importSource

clsx is the supported library by default, and if you have your choice, you can replace it with importSource.

Add the babel configuration

{
  "plugins": [
    [
      "clsx",
      {
        "importSource": "classnames"
      }
    ]
  ]
}

Your code

<div className={['c1', 'c2']} />

After compilation

import _clsx from 'classnames';
<div className={_clsx('c1', 'c2')} />;

options.importName

If your custom import source does not have a default export available, you can specify the import name with importName.

Add the babel configuration

{
  "plugins": [
    [
      "clsx",
      {
        "importSource": "@/utils",
        "importName": "classNames"
      }
    ]
  ]
}

Your code

<div className={['c1', 'c2']} />

After compilation

import { classNames as _clsx } from '@/utils';
<div className={_clsx('c1', 'c2')} />;

Ignore

If you feel that there is an unnecessary transformation, you can add a comment so that it is ignored during the transformation.

Local ignore

You can ignore the conversion of this line by adding a comment above.

Your code

<div className={['c1', 'c2']} />;
<div
  // @clsx-ignore
  className={['c1', 'c2']}
/>;

After compilation

import _clsx from 'clsx';
<div className={_clsx('c1', 'c2')} />;
<div className={['c1', 'c2']} />;

Global ignore

You can omit the conversion of the entire file by adding a comment at the top of the file.

Your code

// @clsx-ignore-global
<div className={['c1', 'c2']} />;
<div className={['c1', 'c2']} />;

After compilation

<div className={['c1', 'c2']} />;
<div className={['c1', 'c2']} />;

Typescript

Support Typescript with jsxImportSource.

You only need to make minor changes to tsconfig.json to support the use of the plugin in Typescript projects.

Only react17+ and Typescript4.7+ are supported due to the use of advanced syntax.

preserve

{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "babel-plugin-clsx/jsx",
    "isolatedModules": true
  }
}

react-jsx

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "babel-plugin-clsx/jsx"
  }
}

react-jsxdev

{
  "compilerOptions": {
    "jsx": "react-jsxdev",
    "jsxImportSource": "babel-plugin-clsx/jsx"
  }
}

react-native

{
  "compilerOptions": {
    "jsx": "react-native",
    "jsxImportSource": "babel-plugin-clsx/jsx",
    "isolatedModules": true
  }
}

One thing to note is that babel-plugin-clsx/jsx only supports type inference, which prevents Typescript from throwing errors.

Examples

React

Nextjs