46

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?

2
  • 2
    I have not worked with next.js but looking at your code I assume that styles is some kind of map and that the correct syntax would be something along the lines of: className={styles["projects-pd-subdetail"] + " " + styles["projects-pd-text"]}
    – Jacob Smit
    Commented Jan 27, 2021 at 3:38
  • Yes, Jacob, this worked. Can you put this in answer form instead of a comment? I want to select your response as the correct answer so you can get credit for it. Commented Jan 27, 2021 at 19:37

12 Answers 12

66

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'
3
  • This throws an error because it thinks I'm subtracting the variables pd and text from styles.projects in the first interpolation and the variables pd and subdetail from styles.proejcts in the second interpolation. Jacob Smit's comment above works though. Commented Jan 27, 2021 at 19:36
  • 1
    @JevonCochran thats why I suggest to use camelCase class name.
    – Robin
    Commented Jan 28, 2021 at 3:44
  • 1
    You can use snake_case works perfectly and is much more readable. Commented Jun 23, 2022 at 6:30
24

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>
2
  • 1
    This is a nice and clean way to use multiple classes. I like this.
    – iqqmuT
    Commented Aug 25, 2022 at 11:18
  • This actually worked for me. Initially, I forgot to add a space between the quotes but now I can create more than one classes in Next.js. Thank you so much for taking out time to write this answer, @rain. I wish you have amazing days ahead :) Commented Aug 27, 2022 at 12:24
18

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"]}`}
1
  • Thanks. That's work with Next.js v13
    – nlavr
    Commented Nov 30, 2022 at 19:48
3

clsx is generally used to conditionally apply a given className.

https://www.npmjs.com/package/clsx

3

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>
2

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))
}

1

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.

1

It worked for me.

<div className={styles.side +" "+ styles.side2}>side2</div>

Thanks to CodenNerd

0

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>
);
0
 <div className={`${GloStyles.flexRow} ${MyStyles.gap_10rem}`}> Hello </div>

Explanation

  • className accept 'String' only
  • use 'String template' solved, it is just like a normally react app className, nothing fancy

enter image description here enter image description here

0

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

-1

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>

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.