I want to use only the pure name of the class without {styles.class-name} convention in Next.js, I google it and find I need to configure the next.config.js file. So, have someone good references for this?
And it works fine in Next.js
This is not working by default in Next.js
Next.js has globals.css file and it's in styles folder so you can make:
className="red"
and then in globals.css file:
.red{
//...
}
But I recommend you to use .module.css files because it's cleaner and doesn't make a mess.
I hadn't checked this earlier. You can directly use babel-plugin-react-css-modules
.
I had earlier wrote a Babel plugin for this. It is not robust, can be improved, but does the job:
// babel-plugin-cx.js
const __path = require('path');
const plugin = ({ types: t }) => {
const cloneNode = t.cloneNode || t.cloneDeep;
return {
name: 'babel-plugin-cx',
visitor: {
Program: {
enter(path, state) {
state.stylesIdentifier = path.scope.generateUidIdentifier('styles');
},
exit(path, state) {
if (state.hasProp) {
const importDeclaration = t.importDeclaration(
[t.importDefaultSpecifier(state.stylesIdentifier)],
t.stringLiteral(
__path
.parse(state.file.opts.filename)
.name.toLowerCase()
.replace(/^([^]*)$/, state.opts.pathReplace),
),
);
path.node.body.unshift(importDeclaration);
}
},
},
JSXAttribute(path, state) {
if (path.node.name.name !== state.opts.propName) return;
if (
state.opts.ignoredElements.includes(
path.findParent((p) => p.isJSXOpeningElement()).node.name.name,
)
)
return;
path.node.name.name = 'className';
const value = path.get('value');
if (value.isLiteral()) {
value.replaceWith(
t.jsxExpressionContainer(
t.memberExpression(
cloneNode(state.stylesIdentifier),
cloneNode(value.node),
true,
false,
),
),
);
state.hasProp = true;
} else if (value.isJSXExpressionContainer()) {
const expression = value.get('expression');
expression.replaceWith(
t.memberExpression(
cloneNode(state.stylesIdentifier),
cloneNode(expression.node),
true,
false,
),
);
state.hasProp = true;
}
},
JSXSpreadAttribute(path, state) {
if (
state.opts.ignoredElements.includes(
path.findParent((p) => p.isJSXOpeningElement()).node.name.name,
)
)
return;
const argument = path.get('argument');
if (!argument.isObjectExpression()) return;
const properties = argument.get('properties');
for (const property of properties) {
if (property.node.key.name === state.opts.propName) {
property.node.key.name = 'className';
const value = property.get('value');
value.replaceWith(
t.memberExpression(
cloneNode(state.stylesIdentifier),
cloneNode(value.node),
true,
false,
),
);
state.hasProp = true;
}
}
},
},
};
};
module.exports = plugin;
// .babelrc
{
"presets": ["next/babel"],
"plugins": [
[
"./babel-plugin-cx",
{
"pathReplace": "./$1.module.css",
"propName": "cx",
"ignoredElements": ["circle", "ellipse", "radialGradient"]
}
]
]
}
If using Typescript, you need to add this (also add this file to includes
array in tsconfig.json
:
// custom.d.ts
import type * as React from 'react';
declare module 'react' {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions, @typescript-eslint/no-unnecessary-qualifier
interface HTMLAttributes<T> extends React.DOMAttributes<T> {
cx?: string;
}
}
After this, you can simply write:
<div cx="red">Red text!</div>
There is no need to import the module file. It will be automatically imported if necessary from the specified "pathReplace"
option. ( $1
indicates the file name using the cx
attribute).
pathReplace
example:
@styles/_$1.module.scss
: if file using cx
is Component.tsx
then the styles will be imported from module @styles/_component.module.scss
.
You can configure the propName
using the provided option. You will also need to change next-env.d.ts
accordingly. And, change the ignoredElements
option accordingly, as your attribute name may be same as some attribute defined in the JSX/HTML standard.
Caveats:
The plugin currently completely ignores className
attribute if cx
is set on the element/component, ie, if you need to merge with some global class name then use className={`global ${styles.red}`}
.
More than one class in cx
is not supported. Can be implemented easily, but I was quite lazy.
Spread attribute is only partially supported:
// supported: <div {...{cx: "red", other: "attribute"}}>Red text!</div> // not supported: const attrs = {cx: "red", other: "attribute"}; <div {...attrs}>Red text!</div>
You may like to configure this rule in ESLint react/jsx-props-no-spreading
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.