Reputation: 1774
I have an unordered list element that looks like this:
<ul className={styles["projects-pd-subdetails-list"]}>
{detail.subdetails.map((sub) => (
<li
className={styles["projects-pd-text projects-pd-subdetail"]}
>
{sub}
</li>
))}
</ul>
With a normal React element, I would be able to apply the multiple classes for the li
element like this:
<li className="projects-pd-text projects-pd-subdetail">{sub}</li>
However, having a space like I do in nextjs means the styles are just getting ignored. How can I fix this problem and properly account for two classNames for my li
element here?
Upvotes: 45
Views: 76560
Reputation: 25120
In Nextjs 13.4+ you can use default supported cn
which internally uses clsx
layout.tsx
import { cn } from "@/lib/utils";
export default function Component(){
return <div className={cn("flex font-sans","inter.className","h-full")}/>
}
By default generated file: @ lib/utils.ts
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Upvotes: 2
Reputation: 5427
You can use multiple className
like this
<li className={`${styles.projects-pd-text} ${styles.projects-pd-subdetail}`}>
{sub}
</li>
But there is a problem. It may throws an error(I guess, not sure). You may use camelCase
in your css className.
<li className={`${styles.projectsPdText} ${styles.projectsPdSubdetail}`}>
{sub}
</li>
or, if you don't want to camelCase
<li className={`${styles["projects-pd-text"]} ${styles["projects-pd-subdetail"]}`}>
{sub}
</li>
Let me know if it works.
And another convenient way of using multiple classes using clsx library. The good part of clsx
is - you can handle conditional class name also.
// Strings (variadic)
clsx('foo', true && 'bar', 'baz');
//=> 'foo bar baz'
// Objects
clsx({ foo:true, bar:false, baz:isTrue() });
//=> 'foo baz'
// Objects (variadic)
clsx({ foo:true }, { bar:false }, null, { '--foobar':'hello' });
//=> 'foo --foobar'
// Arrays
clsx(['foo', 0, false, 'bar']);
//=> 'foo bar'
// Arrays (variadic)
clsx(['foo'], ['', 0, false, 'bar'], [['baz', [['hello'], 'there']]]);
//=> 'foo bar baz hello there'
// Kitchen sink (with nesting)
clsx('foo', [1 && 'bar', { baz:false, bat:null }, ['hello', ['world']]], 'cya');
//=> 'foo bar hello world cya'
Upvotes: 66
Reputation: 601
A simple array join should suffice.
["class1", "class2", "class3"].join(" ")
result: "class1 class2 class3"
<li className={[styles.projects_pd_text, styles.projects_pd_subdetail].join(" ")}>
{sub}
</li>
Or save it as a variable for re-using :>
const listClasses = [styles.projects_pd_text, styles.projects_pd_subdetail]
// somewhere in the code
<li className={[...listClasses, styles.projects_pd_outline].join(" ")}>
{sub}
</li>
Upvotes: 24
Reputation: 63
The array join()
is the cleanest solution, as @rain mentioned here.
You can also use global css class names.
<span className={[styles.grid, "disabled"].join(" ")}>content</span>
Upvotes: 3
Reputation: 81
a Function would be much cleaner
const convertToNextClassName = (className) => className.split(' ').map(c => styles[c]).join(' ')
then in the jsx you can directly call it
<div className={convertToNextClassName("firstClass secondClass")}></div>
for conditional i suggest using classnames from npm
Upvotes: 0
Reputation: 59
<div className={`${GloStyles.flexRow} ${MyStyles.gap_10rem}`}> Hello </div>
Explanation
enter image description here enter image description here
Upvotes: 0
Reputation: 126
It worked for me.
<div className={styles.side +" "+ styles.side2}>side2</div>
Thanks to CodenNerd
Upvotes: 1
Reputation: 111
If you console log your css or scss module class (ex. ImportedStyleModule.someClassName) you'll see it's just a string that has an auto generated UID concatenated.
Ergo, it's just a string so you can use a number of ways to join them like so:
//ts
const mergeStyles = (styleArray: string[]) => (styleArray.map((style: string) => `${style}`).join(" "));
//js
const mergeStyles = (styleArray) => (styleArray.map((style) => `${style}`).join(" "));
//example
return (
<span
onClick={() => setIsClicked(!isClicked)}
className={`${!!isClicked
? mergeStyles([NavbarStyle.navButton, NavbarStyle.navButtonOpen])
: NavbarStyle.navButton}`}
>
<Image src='/image-logo.svg' alt='logo' width='80px' height='40px' />
</span>
);
Upvotes: 0
Reputation: 47
Because it might be tedious to always write styles.className
for every class you need to add to an element, you can create a utility function that makes things look neater.
For example, in my case, I created this function:
export const classes = (styles: any, classes: string) => {
const list = classes.split(' ');
classes = '';
for (const className of list) {
classes += `${styles[className] }`
}
return classes;
}
in a util file.
And on elements, I can do this
<div className={classes( styles, 'hero-container text-success text-bold italicize another-class yet-another-class ')}>
Lorem ipsum dolor
</div>
A PLUS:
One other issue around classnames I encountered getting started with NextJS on VSCode was getting emmet tag generation to list the classnames the NextJS way when I type css class selector.
"Complete NextJS Classname Emmet": {
"scope": "javascript,typescript,typescriptreact,javascriptreact",
"prefix": ["."],
"body": [
"<div className={classes( styles, '$1')}>",
"\t$2",
"</div>"
],
"description": "Lean autocomplete emmet in nextjs css modules classname"
}
I added this to my VSCode > Preferences > User Snippets > typescriptreact.json
These made working with classnames in NextJS easier for me - especially on VSCode.
Upvotes: 1
Reputation: 143
This is how it works for me in my style component
<ul>
<li className={router.pathname == "/" && styles.active}><Link href="/">Home</Link></li>
<li className={router.pathname == "/about" && styles.active}><Link href="/about">About</Link></li>
<li className={router.pathname == "/contact" && styles.active}><Link href="/contact">Contact</Link></li>
<li><Link href="/404">404</Link></li>
</ul>
Upvotes: -1
Reputation: 2379
As stated in my original comment I have not worked with Next.js.
It appears as though styles is a map of some kind i.e.:
const styles = {
"projects-pd-subdetails-list": "Class Name A",
"projects-pd-text": "Class Name B",
"projects-pd-subdetail": "Class Name C"
}
This means that by using a line similar to styles["projects-pd-text projects-pd-subdetail"]
you are attempting to retrieve the value for the key "projects-pd-text projects-pd-subdetail"
which does not exist.
I would suggest retrieving the values individually from the map and then joining them together with your choice of string concatenation.
className={styles["projects-pd-subdetail"] + " " + styles["projects-pd-text"]}
// OR
className={`${styles["projects-pd-subdetail"]} ${styles["projects-pd-text"]}`}
Upvotes: 18
Reputation: 51
clsx is generally used to conditionally apply a given className.
https://www.npmjs.com/package/clsx
Upvotes: 3