I'm using the following code to dynamically set a className in a React component based upon a boolean from props:
<div className={this.props.menuOpen ? 'inactive' : 'active'}>
...
</div>
However, I'm also using CSS Modules, so now I need to set the className to:
import styles from './styles.css';
<div className={styles.sideMenu}>
...
</div>
I'm having trouble with this - I tried using classnames to gain more control with multiple classes, but because I need the end result to be that the className is set to both styles.sideMenu AND styles.active (in order for CSS Modules to kick in) I'm unsure how to handle this.
Any guidance is greatly appreciated.
Using classnames and es6:
let classNames = classnames(styles.sideMenu, { [styles.active]: this.props.menuOpen });
Using classnames and es5:
var classNames = classnames(styles.sideMenu, this.props.menuOpen ? styles.active : '');
Bit late to the party here, but using string templates works for me - you could move the ternary operator out to a const if you'd like as well:
<div className={`${styles.sideMenu} ${this.props.menuOpen ? styles.inactive : styles.active}`>
...
</div>
I wanted to just add on a better way of using the bind api of classnames npm. You can bind the classnames to the styles object imported from css like below:
import classNames from 'classnames/bind';
import styles from './index.css';
let cx = classNames.bind(styles);
and use it like this:
cx("sideMenu", "active": isOpen)
where sideMenu and active are in styles object.
Using logical AND instead of ternary operator makes it even less verbose since classnames omits a falsy value.
<div className={ classNames(styles.sideMenu, this.props.menuOpen && styles.active) }></div>
This is the closest I can get to a working solution:
const isActive = this.props.menuOpen ? styles.inactive : styles.active;
<div className={isActive + ' ' + styles.sideMenu}>
This does work - both allow the styles in the imported stylesheet to be used, and is only applied when this.props.menuOpen is true.
However, it's pretty hacky - I'd love to see a better solution if anyone has any ideas.
Using Array.join
<div className={[styles.sideMenu, this.props.menuOpen ? styles.show : styles.hide].join(' ')}></div>
While I'm not an expert on CSS modules, I did find this documentation: https://github.com/css-modules/css-modules/blob/master/docs/import-multiple-css-modules.md
It appears that you'll need to combine the styles for active and sideMenu together using Object.assign
import classNames from 'classnames/bind'.
then you can use like this:
let cx = classNames.bind(styles);
In case like that styles['bar-item-active'] , you can wrap it . in second square brackets like [styles['bar-item-active']] : your condition
I don't think anyone has suggested using both the style and the className attributes in your React DOM element:
const sideMenu={backgroundColour:'blue',
border:'1px black solid'}
return <>
<div style={sideMenu} className={this.props.menuOpen ? styles.inactive : styles.active}>
...
</div>
</>
It's not the neatest solution, but it does avoid adding another dependency to your project and if your sideMenu class is small then it could be a option
Using classnames library
import classNames from "classnames";
classNames is a function. if you pass some strings, it joins them together if they all are truthy. If there is any falsy string it will not pass it. For example
let textColor=undefined
classNames(textColor, "px-2","py-2" )
since textColor variable is falsy, classNames will ignore it and returns this string
"px-2 py-2"
You can also pass an object to classNames function.
const active=true
const foo=false
Let's say I have this expression
classNames({
'text-green':active,
'text-yellow':foo
})
classNames function will look at "VALUE" of each pair. if the "value" is truthy, it will take the "KEY" but if the "VALUE" is falsy it will ignore the "KEY". In this example since "active" true and "foo" is false it will return this string
'text-green'
In your example, you want to add a className based on if the prop is truth or falsy in other words if the prop exists or not:
let classNames=classNames(
styles.sideMenu,
// based on props that you are passing you can define dynamic classnames in your component
{
"active":this.props.menuOpen
}
)
Related
I am looking for a solution that will allow some styling options in a CMS that can dynamically change the classes of specific components in my NextJS app. My code looks like this:
pages/index.js:
...
import client from "../lib/client";
const Home = ({ headerConfig }) => {
return (
<>
<Header headerConfig={headerConfig} />
...
</>
);
};
export const getServerSideProps = async () => {
const headerConfig = await client.getDocument("headerConfig");
return {
props: { headerConfig },
};
};
export default Home;
components/Header.jsx:
const Header = ({ headerConfig }) => {
return (
<nav className={`relative ... ${headerConfig.bgColour}`}>
...
</nav>
);
}
export default Header
However, the styling does not apply and the background colour remains unchanged although the class does seem to be injected into the class attribute on the browser.
I know my current method is incorrect but I am clueless as to how to fix this. Could someone help point me in the right direction?
I assume that you are using tailwind. If so, you cannot inject classnames into an html element. This is because tailwind only includes classes that are explicitly declared somewhere within your code (it will find classes within any string in your project depending on the configuration). You can get around this problem by adding classes to the safelist array in your tailwind.config.js file. You can also safelist classes with regex to allow all variants of certain utilities.
However, safelisting only works if there are a specific set of classes that could potentially be injected. One option, which will be guaranteed to work but NOT RECOMMENDED, is to add a <link> in your html to the tailwind cdn. However this will include every single tailwind class in your css bundle, making it MUCH larger and your website slower.
Another solution is to use inline styles which are calculated with javascript depending on the classes you need to inject. If you are dealing with only simple parts of tailwind (like padding, margin, or other sizing units), this may be a good approach. For example a class like p-4 would get converted to padding: 1rem in your inline styles.
Depending on the needs of your application, one of these three approaches is probably the way to go. Hope this helps!
Hello I try to understand when it's necessary to use inline style instead className in this case. I take a long time to solve my problem of translation. At the beginning I want to translate component by using classNameand that's don't work. it's very weird because in my point of view there is no reason that's happen. So I figure there is something wrong in my code, but what... I have not yet found. So I finish trying to translate by using a inline style. Miracle, that's work fine.
My question is why ?
Work well
export function Content() {
return (
<div style={{transform: 'translateY(100px)'}}>
<Test/>
<Footer />
</div>)
}
don't work
export function Content() {
return (
<div className={container_content}>
<Test/>
<Footer />
</div>
)
}
css
.container_content {
transform: translateY(100px);
}
Nota bene :
The problem is not from the method. To use className in my jsx
must be like that:
import { container_content } from "./test.module.css";
and next
<div className={container_content}><div>
So this part of code is good, the issue seems to come from elsewhere...
What's happening is that when you use the inline style you are passing an object that includes the styling for that component. When you use the className you need to pass in a string for the class you want to use. Right now you are passing a variable name. Either of these works:
<div className={"container_content"}>
OR
<div className="container_content">
If you think about it in regular html you would do
<div class="container_content">
EDIT: Given your updated question, you should just import the css file with:
import "./test.module.css"
and then use the solution I mentioned.
inside the js file, you need to import the CSS file like this
import " css-file-dir";
and then you can Reference to the CSS classes inside your component as a string
example :
className="container_content"
I have a useState and try to render my Modal windows based on it.
Here is my code and I used css modules for my classes.
The thing is I am not able to find the correct syntax to doing so.
I ended up writing this but it does not work.
export default function Modal({ content }) {
const [showModal, setShowModal] = useState(true);
return (
<div className={`${showModal} ? ${styles.ModalWindow} : ${styles.DisplayNone}`} </div>
The backtick (`) symbol indicates a template literal/string in JavaScript. You would only want to use this feature if you want the class to literally be something like "condition ? window-class : hidden".
In this case, you want the expression to be evaluated, not converted into a string literal. So just get rid of those template literal markers:
<div className={showModal ? styles.ModalWindow : styles.DisplayNone} </div>
I am trying to send in a style property to a component and not sure what the difference is..
Why does this statement work and applies the formatting
<Toggle
className={styles.toggle_show} // class name gets passed as Map_toggle_show__17ukO and applied
/>
but this statement throws errors
<Toggle
className={isShow ? {styles.toggle_show} : {styles.toggle_hide}}
/>
If I update the second statement to
<Toggle
className={isShow ? "styles.toggle_show" : "styles.toggle_hide"}
/>
The classNames do get passed to the component but css dont get applied.
Also why is this message coming on the const declaration
import styles from "./Map.module.scss";
const [ToggleVisibilyClass, setToggleVisibilyClass] = React.useState(
{styles.toggle_show}
);
(property) styles: {
readonly [key: string]: string;
}
Parsing error: ',' expected.eslint
Try this:
className={isShow ? styles.toggle_show : styles.toggle_hide}
The correct way in your case will look like this:
<Toggle
className={isShow ? styles.toggle_show : styles.toggle_hide}
/>
styles is actually an object with string properties, so {styles.toggle_show} is not valid syntax, that's why you are getting an error.
And this "styles.toggle_show" is working because it's a string, but it's not the correct string. Behind the scenes Webpack generates a different name for your classes, usually a unique one and you can access it with styles.toggle_show.
Also if you have some more complicated scenarios try using the classnames package it can make things easier and it have a lot of examples.
I am using a REACT BIG CALENDAR and I want to get access to the css values in one of my functions.
I created a style component and override the library
const StyledCalendar = styled(Calendar);
Now for example there is a div inside of the Calendar with the class = "hello",
How would I access the css values of "hello" in a function? Similar to property lookup say in stylus.
I have tried window.getComputedStyle(elem, null).getPropertyValue("width") but this gives the css of the parent component.
If you know the class name, you should be able to select that and give that element to getComputedStyle instead of giving it StyledCalendar. Something like:
const childElement = document.getElementsByClassName('hello')[0];
const childWidth = getComputedStyle(childElement).getPropertyValue('width');
(this assumes that there's only one element with the class 'hello' on the page, otherwise you'll have to figure out where the one you want is in the node list that's returned by getElementsByClassName)
You can do it using simple string interpolation, just need to be sure that className is being passed to Calendar's root element.
Like this:
const StyledCalendar = styled(Calendar)`
.hello {
color: red;
}
`
Calendar component
const Calendar = props => (
// I don't know exact how this library is structured
// but need to have this root element to be with className from props
// if it's possible to pass it like this then you can do it in this way
<div className={props.className}>
...
<span className="hello"> Hello </span>
...
</div>
)
See more here.