MUI v5 - Seperate styling from component file - css

I want to separate styling from component File in MUI v5. The way I did it in v4 was using makeStyles like that:
Page.style.js:
import { makeStyles } from "#material-ui/core";
export const useStyles = makeStyles({
root: {
background: "white",
},
});
Page.js:
import React from "react";
import useStyles from "./Page.style";
const Page = () => {
const classes = useStyles();
return (
<div className={classes.root}></div>
)
}
makeStyles is now legacy and I heard it will be deprecated next version.
What is the proper way to separate styling and component into different files in v5?

The recommended styling APIs in v5 are styled()/sx prop. If you use styled, you can separate the styling code by creating a reusable styled component. The sx prop is more suitable for inline one-off style so it's not the best choice to use in this scenario:
const Div = styled('div')({
background: "white",
});
export Div;
import React from "react";
import { Div } from "./Div";
const Page = () => {
return (
<Div />
)
}
Besides that, you can also use variant in MUI v5. The way it works is you create a custom styles and assign a name for it called variant so instead of specifying a className like before, you set the variant prop like this:
<Button variant="myCustomVariant">
Button
</Button>
Custom variant can be created using createTheme. See this answer for more detail. Be aware that for now, this feature is not supported in all components:
const theme = createTheme({
components: {
MuiButton: {
variants: [
{
props: { variant: 'myCustomVariant' },
style: {
textTransform: 'none',
border: `2px dashed grey${blue[500]}`,
},
},
],
},
},
});

I would recommend CSS Modules or Plain CSS because the other styling solution might get deprecated in the near future, one good example is the makeStyle which is now considered a Legacy styling solution.
You can find more information in Mui's webpage under the Style library interoperability section.

It is suggested to use either sx or styled.
Document: https://mui.com/system/basics/

Related

Tailwind style doesn't seems to be apply inside Material-ui Drawer component in NextJs

I am trying to add styling inside a #mui/material/drawer component using tailwind.
import { Close } from "#mui/icons-material";
import { Box, Drawer, IconButton, TextField } from "#mui/material";
import { useContext} from "react";
import { SearchContext } from "store/context";
export default function SearchDrawer() {
const { search, setSearchMode } = useContext(SearchContext);
return (
<Drawer open={search.searchMode} anchor="bottom">
<Box sx={{ height: "100vh" }} className="bg-red-500">
<IconButton onClick={() => { setSearchMode(!search.searchMode); }}><Close/></IconButton>
<div>
<TextField variant="outlined" sx={{display: 'block'}}/>
<TextField variant="outlined" sx={{display: 'block'}}/>
</div>
</Box>
</Drawer>
);
}
Expected behavior is
<Box sx={{ height: "100vh" }} className="bg-red-500">
This line of code will make whole screen red, But Nothing happen.
Output after render
Tailwind styles are not applying on some of Material-ui components like "Drawer", "Dialog", "ToolTip" This all components are hover over other components.
Tailwindcss classes are not working in Tooltip & Dialog components #33424 - GitHub
This page says to modify material-ui theme,
defaultProps: {
container: rootElement,
},
},
But rootElement is available in Reactjs, How to do it on Nextjs.
this is what worked for me from the official docs :
Remove Tailwind CSS's preflight style so it can use the MUI's preflight instead (CssBaseline).
In your file tailwind.config.js add the following :
//Remove Tailwind CSS's preflight style so it can use the MUI's preflight instead
corePlugins: {
preflight: false,
},
Add the important option, using the id of your app wrapper. For example, #__next for Next.js and "#root" for CRA:
In your file tailwind.config.js add the following (for nextjs add #__next):
//Add the important option, using the id of your app wrapper. For example, #__next for Next.js and "#root" for CRA
important: '#__next',
Your file tailwind.config.js should like like :
/** #type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
//Add the important option, using the id of your app wrapper. For example, #__next for Next.js and "#root" for CRA
important: '#__next',
theme: {
extend: {},
},
plugins: [],
//Remove Tailwind CSS's preflight style so it can use the MUI's preflight instead
corePlugins: {
preflight: false,
},
}
Fix the CSS injection order. Most CSS-in-JS solutions inject their styles at the bottom of the HTML , which gives MUI precedence over Tailwind CSS. To reduce the need for the important property, you need to change the CSS injection order. Here's a demo of how it can be done in MUI:
import * as React from 'react';
import { StyledEngineProvider } from '#mui/material/styles';
export default function GlobalCssPriority() {
return (
<StyledEngineProvider injectFirst>
{/* Your component tree. Now you can override MUI's styles. */}
</StyledEngineProvider>
);
}
You can check the official documentation : https://mui.com/material-ui/guides/interoperability/#tailwind-css

Material UI 5 class name styles

I migrated from Mui 4 to 5 and wonder how to use class names. If I want to apply certain styles to just one component there is the SX property. However, I'm struggling with using the same class for multiple components. In v4 my code looked like this:
export const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
padding: theme.spacing(1),
margin: 'auto',
},
})
)
I could import this useStyles hook in any component and use it like this:
const classes = useStyles()
...
<div className={classes.root}>...</div>
This docs say, that I can 'override styles with class names', but they don't tell how to do it:
https://mui.com/customization/how-to-customize/#overriding-styles-with-class-names
Do I have to put these styles in an external CSS file?
.Button {
color: black;
}
I would rather define the styles in my ts file.
I also found this migration guide:
https://next.material-ui.com/guides/migration-v4/#migrate-makestyles-to-emotion
I don't like approach one, because using this Root wrapper, it is inconvenient to apply a class conditionally. (Especially for typescript there is some overhead) Approach two comes with an external dependency and some boilerplate code.
Ideally I would use styles like this, perhaps with one rapper function around the styles object:
export const root = {
padding: theme.spacing(1),
margin: 'auto',
}
<div className={root}>...</div>
Of course, the last approach doesn't work, because className wants a string as input. Does anybody know an alternative with little boilerplate code?
I suggest you take a look at emotion's documentations for details. The sx prop is actually passed to emotion.
You can do something like this:
const sx = {
"& .MuiDrawer-paper": {
width: drawerWidth
}
};
<Drawer sx={sx}/>
Equivalent to MUI v4
const useStyles = makeStyles({
drawerPaper: {
width: drawerWidth,
}
});
const classes = useStyles();
<Drawer
classes={{
paper: classes.drawerPaper,
}}
/>
Answering your exact question, there are use cases (I think yours is not one of them and you should use styled components) however for those like me who stumble upon it and want a "exact answer to this question" and not a "do this instead", this is how you achieve to retrieve the class names.
This is so far undocumented.
For functional components, using emotion, here an use case where you have a 3rd party component that expects, not one, but many class names, or where the className property is not where you are meant to pass the property.
import { css, Theme, useTheme } from "#mui/material/styles";
import { css as emotionCss } from "#emotion/css";
const myStyles = {
basicClass: {
marginLeft: "1rem",
marginRight: "1rem",
paddingLeft: "1rem",
paddingRight: "1rem",
},
optionClass: (theme: Theme) => ({
[theme.breakpoints.down(theme.breakpoints.values.md)]: {
display: "none",
}
})
}
function MyComponent() {
cons theme = useTheme();
// first we need to convert to something emotion can understand
const basicClass = css(myStyles.basicClass);
const optionClass = css(myStyles.optionClass(theme));
// now we can pass to emotion
const basicClassName = emotionCss(basicClass.styles);
const optionClassName = emotionCss(optionClass.styles);
return (
<ThirdPartyComponent basicClassName={basicClassName} optionClassName={optionClassName} />
)
}
When you have a Class Component, you need to use the also undocumented withTheme from #mui/material/styles and wrap your class, if you use the theme.
WHEN IT IS NOT AN USE CASE
When your component uses a single className property just use styled components.
import { styled } from "#mui/material/styles";
const ThrirdPartyStyled = styled(ThirdPartyComponent)(({theme}) => ({
color: theme.palette.success.contrastText
}))
Even if you have dynamic styles
import { styled } from "#mui/material/styles";
interface IThrirdPartyStyledExtraProps {
fullWidth?: boolean;
}
const ThrirdPartyStyled = styled(ThirdPartyComponent, {
shouldForwardProp: (prop) => prop !== "fullWidth"
})<IThrirdPartyStyledExtraProps>(({theme, fullWidth}) => ({
color: theme.palette.success.contrastText,
width: fullWidth ? "100%" : "auto",
}))
Even if each one has some form of custom color, you just would use "sx" on your new ThrirdPartyStyled.
When you are just trying to reuse a style around (your use case)
const myReusableStyle = {
color: "red",
}
// better
const MyStyledDiv = styled("div")(myReusableStyle);
// questionable
const MySpanWithoutStyles = styled("span")();
// better
const MyDrawerStyled = styled(Drawer)(myReusableStyle);
function MyComponent() {
return (
<MyStyledDiv>
questionable usage because it is less clean:
<MySpanWithoutStyles sx={myReusableStyle}>hello</MySpanWithoutStyles>
<MySpanWithoutStyles sx={myReusableStyle}>world</MySpanWithoutStyles>
these two are equivalent:
<MyDrawerStyled />
<Drawer sx={myReusableStyle} />
</MyStyledDiv>
)
}
Now what is "presumably" cool about this is that your style, is just an object now, and you can just import it and use it everywhere without makeStyles or withStyles, supposedly an advantage, even when to be honest, I have never used that of exporting/importing around; the code seems a bit cleaner nevertheless.
You seem to want to use it so all you do is.
export const myStyles {
// your styles here
}
because this object is equivalent in memory, and it is always the same object, something that is easier to mess up with styles, it should be as effective or even more than your hook, theoretically (if it re-renders often even when setup may be longer), which stores the same function in memory but returns a new object every time.
Now you can use those myStyles everywhere you deem reasonable, either with styled components or by assigning to sx.
You can further optimize, say if it's always a div that you use that is styled the same way, then the styled component MyStyledDiv should be faster, because it is the same and done each time. How much faster is this? According to some sources 55% faster, to me, it is taking 4 weeks of refactor and the JSS compatibility with emotion is bad, all mixed with SSR is making everything unusable and slow and broken, so let's see until then when the whole is refactored.
Here is a pattern that I've found useful in MUI 5. It allows you to keep style definitions in the same file but isolated, & avoids repeated function calls for every CSS property where you need to access your theme (e.g. width: ({ spacing }) => spacing(12))). It also feels similar to MUI's native CSS API.
Create a function that takes your theme as an argument & returns an object of named style groups. Then reference those groups directly in your sx props. This also allows for the use of classNames in a way similar to Material-UI 4.
import { useTheme } from '#mui/material';
import clsx from 'clsx';
export const NavItem = (props) => {
// Bring in style groups
const sx = styles(useTheme());
// Define classNames
const classNames = clsx({
isActive: props.isActive
});
return (
{/* Use classNames and style groups */}
<ListItemButton className={classNames} sx={sx.button}>
<ListItemAvatar sx={sx.avatar}>{props.icon}</ListItemAvatar>
<ListItemText>{props.label}</ListItemText>
</ListItemButton>
);
}
// Define style groups
function styles(theme) => {
return {
button: {
paddingX: 6,
'&.isActive': {
backgroundColor: theme.palette.secondary.light
}
},
avatar: {
'.isActive &': {
border: '2px solid green'
}
}
};
}
I'm in the same boat, about six months behind, i.e., starting to make the transition to v5 from v4 now... Just when I thought I had a handle on it all!
Having read this post and trying a few things out, I was able to replicate the ability to re-use a chunk of css. I'm a big fan of what used to be the overrides prop; that feature hasn't gone away, it's just under a different prop (loosely speaking). Regardless, I mention it because it provides access to what I like a lot about css: selectors.
To hit all MUI-Drawers my pref is for whatever the new overrides is. For targeted reuse of css I like the following:
import { reuseThisCss } from 'sharedCss';
export default styled(Drawer)(({ theme, ownerState }) => {
...
return {
'& .MuiDrawer-paper': {
boxShadow: xxl,
border: 'none',
'& .MuiListItemText-root': reuseThisCss,
},
};
export default ThisSpecificDrawerVariant;
Note: The focus is not on using styled (It's not my goto approach).
The css in the return value is the equivalent to the following css: .MuiDrawer-paper .MuiListItemText-root {...}.
This says, "select all .MuiListItemText-root under the .MuiDrawer-paper parent. If I want to optimize the render, while increasing the dependency on a specific hierarchy, I'll specify/expand on the selector that much more with whatever lies between the .MuiDrawer-paper and MuiListItemText-root. For instance, in my case:
...
return {
'& .MuiDrawer-paper': {
boxShadow: xxl,
border: 'none',
'& > a > li > div > .MuiListItemText-root': reuseThisCss,
},
};
Finally, per a question in the comments, generally this will not prevent a nested application of the style. In my experience, marking each level with a className is useful. I only "mark" the element that signals the start of a new level. So, if it were Drawer in the above example, I would start the css selector with .MUI-Drawer.level-3. The rest of css remains the same.
I still have not figured out if whether setting the className dynamically remains a performant and sufficiently flexible goto... TBD.
If you are using makeStyles or withStyles to provide CSS class, you can follow the instruction below.
CSS overrides created by makeStyles

ReactJS advanced custom styling

Using reactjs only, is it possible to do advanced styling similar to
#primary-nav .list-group-item.active {}
// or
#secondary-nav .list-group-item:hover>.background-item {}
in the first example I could do some rather simple javascript logic to figure out if the component is "active" but on the second example it's just so much simpler with css.
Is there a clear react+js solution for these situations that comes close to the simplicity of css?
className is applied exactly like a regular HTML class. So to correctly target .background-image like in
.list-group-item:hover>.background-item
Your jsx structure should look like
import './index.css'
const Component = () =>{
return(
<div className='list-group-item'>
<div className='background-item' />
<span>
<div className='background-item' /> /*Targeting nested items '>' */
</span>
</div>
)
}
You can use jss and clsx to have dynamic and conditional styles. Here is an example using MUI styles(hooks API), but you can use styled components, react-jss or implement you're own style's solution based on jss.
import { makeStyles } from '#material-ui/styles'
import clsx from 'clsx'
const styles = {
root:{
color: 'white',
'&:active':{
color: 'red'
}
},
hidden:{
opacity: 0
}
}
const useStyles = makeStyles(styles)
const Component = ({ open }) =>{
const classes = useStyles()
const rootStyle = clsx({
[classes.root] : true,
[classes.hidden] : !open
})
return <div classsName={rootStyle} />
}
jss also have lots of cool features like theming support, styles interpolation (a personal favorite), nested selectors, style's rules,etc. Definitely worth taking a look.
Notice that clsx doesn't require jss to work, it's just a helper to conditionally apply classes. You can use it like clsx({'foo' : true, 'bar': false})

Styling material UI components

Not really a problem but something Iā€™m not happy with. I'm using react + typescript + css modules + https://material-ui-next.com/. Problem is that when I need to style material ui components I have to use !important a lot. Question is if there is a way to create styles without important. I create a sample project to reproduce the problem https://github.com/halkar/test-css-modules
material-ui exposes many of their components for styling. There two ways to go about doing this.
Apply styles globally
You could style the components globally and apply it to the theme. An example of this would be something like this (copied from the docs http://www.material-ui.com/#/customization/themes):
import React from 'react';
import {cyan500} from 'material-ui/styles/colors';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import AppBar from 'material-ui/AppBar';
// This replaces the textColor value on the palette
// and then update the keys for each component that depends on it.
// More on Colors: http://www.material-ui.com/#/customization/colors
const muiTheme = getMuiTheme({
palette: {
textColor: cyan500,
},
appBar: {
height: 50,
},
});
class Main extends React.Component {
render() {
// MuiThemeProvider takes the theme as a property and passed it down the hierarchy
// using React's context feature.
return (
<MuiThemeProvider muiTheme={muiTheme}>
<AppBar title="My AppBar" />
</MuiThemeProvider>
);
}
}
export default Main;
As you can see in here, appBar component have a height of 50px meaning that every time you add an appbar component to your app down the tree where you applied the muiTheme, it will give it a height of 50px. This is a list of all the styles you can apply for each component https://github.com/callemall/material-ui/blob/master/src/styles/getMuiTheme.js.
Apply styles using style attribute
To apply the styles to individual components, you can usually use the style property and pass it the styles you want.
This is another example from the docs where a margin of 12px is applied to a RaisedButton.
import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
const style = {
margin: 12,
};
const RaisedButtonExampleSimple = () => (
<div>
<RaisedButton label="Default" style={style} />
<RaisedButton label="Primary" primary={true} style={style} />
<RaisedButton label="Secondary" secondary={true} style={style} />
<RaisedButton label="Disabled" disabled={true} style={style} />
<br />
<br />
<RaisedButton label="Full width" fullWidth={true} />
</div>
);
export default RaisedButtonExampleSimple;
Now, the styles are defined in the same file but you could define them in a separate file and import them to the file where you are using the components.
If you want to apply multiple styles then you can use the spread operator like so: style={{...style1,...style2}}.
Usually, you are styling a specific thing in the component (root element) with the style property but some components have more than one property to style different elements of the component. Under properties in this page http://www.material-ui.com/#/components/raised-button, you can see that there are style property, labelStyle and rippleStyle to style different parts of RaisedButton.
Check the properties under the component that you are using and see which style property you could use, otherwise check the available global style properties you could override. Hope this helps!
I should've used JssProvider and tell it to put material UI styles before mine in the page head section.
import JssProvider from 'react-jss/lib/JssProvider';
import { create } from 'jss';
import { createGenerateClassName, jssPreset } from 'material-ui/styles';
const generateClassName = createGenerateClassName();
const jss = create(jssPreset());
// We define a custom insertion point that JSS will look for injecting the styles in the DOM.
jss.options.insertionPoint = document.getElementById('jss-insertion-point');
function App() {
return (
<JssProvider jss={jss} generateClassName={generateClassName}>
...
</JssProvider>
);
}
export default App;
you have to use the component API's. You can't set style to the components imported from libraries just with css if the component has API's to get style.
*Update
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import Button from 'material-ui/Button';
const styles = {
root: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px',
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .30)',
},
label: {
textTransform: 'capitalize',
},
};
function Classes(props) {
return (
<Button
classes={{
root: props.classes.root, // class name, e.g. `classes-root-x`
label: props.classes.label, // class name, e.g. `classes-label-x`
}}
>
{props.children ? props.children : 'classes'}
</Button>
);
}
Classes.propTypes = {
children: PropTypes.node,
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Classes);

conditional css in create-react-app

I have default css file and separate css file that should be applied (to owerride default) only when certain conditions are met.
I am using create-react-app wit default import 'file.css' syntax.
What is the best way forward to decide whether to load or not load particular css file dynamically?
The require method only worked in development (as all the CSS is bundled upon build), and the import method did not work at all (using CRA version 3.3).
In our case, we have multiple themes, which cannot be bundled - so we solved this using React.lazy and React.Suspense.
We have the ThemeSelector, which loads the correct css conditionally.
import React from 'react';
/**
* The theme components only imports it's theme CSS-file. These components are lazy
* loaded, to enable "code splitting" (in order to avoid the themes being bundled together)
*/
const Theme1 = React.lazy(() => import('./Theme1'));
const Theme2 = React.lazy(() => import('./Theme2'));
const ThemeSelector: React.FC = ({ children }) => (
<>
{/* Conditionally render theme, based on the current client context */}
<React.Suspense fallback={() => null}>
{shouldRenderTheme1 && <Theme1 />}
{shouldRenderTheme2 && <Theme2 />}
</React.Suspense>
{/* Render children immediately! */}
{children}
</>
);
export default ThemeSelector;
The Theme component's only job, is to import the correct css file:
import * as React from 'react';
// šŸ‘‡ Only important line - as this component should be lazy-loaded,
// to enable code - splitting for this CSS.
import 'theme1.css';
const Theme1: React.FC = () => <></>;
export default Theme1;
The ThemeSelector should wrap the App component, in the src/index.tsx:
import React from 'react';
import ReactDOM from 'react-dom';
import ThemeSelector from 'themes/ThemeSelector';
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
As I understand, this forces each Theme to be split into separate bundles (effectively also splitting CSS).
As mentioned in the comments, this solution does not present an easy way of switching themes runtime. This solution focuses on splitting themes into separate bundles.
If you already got themes split into separate CSS files, and you want to swap themes runtime, you might want to look at a solution using ReactHelmet (illustrated by #Alexander Ladonin's answer below)
You can use require('file.css') syntax instead. This will allow you to put it inside of a conditional.
e.g.
if(someCondition) {
require('file.css');
}
Use React Helmet. It adds links, meta tags etc into document header dynamically.
Add it into any render method.
import {Component} from 'react';
import ReactHelmet from 'react-helmet';
class Example extends Component{
render(
<ReactHelmet link={
[{"rel": "stylesheet", type:"text/css", "href": "/style.css"}]
}/>);
}
}
You can rewrite it on next <ReactHelmet/> rendering.
One simple solution that I found that works in production is to use vercel's styled-jsx. First, install styled-jsx:
npm install --save styled-jsx
Or if you use Yarn:
yarn add styled-jsx
Now create strings from your css file, so for instance:
const style1 = `
div {
display: flex;
flex-direction: column;
align-items: center;
}
`
const style2 = `
div {
display: flex;
flex-direction: column;
align-items: center;
}
`
And then in your React Component, you can do something like this:
const MyComponent = () => {
return (
<div className='my-component'>
<style jsx>
{
conditionA ? style1: style2
}
</style>
</div>
)
}
Simply add <style jsx>{your_css_string}</style> to the component which you wish to add styling to and you can then to implement conditions just use different strings to import different css styling.
If you are here you most likely are trying to condition a CSS or SCSS import, probably to make some light/dark mode theme or something. The accepted answer works just on mount, after the second css is loaded they are both loaded and you dont have a way to unload them, or actually you have, keep reading...
The use of React lazy and suspense is awesome but in this case we need to help our selves from webpack, because is actually the guy that bundles stuff and can also unbundle stuff, which is what you need, a toggle of css imports basically
Adding webpack lazyStyleTag
Go to your webpack config file and add the following rules
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
// Probly you already have this rule, add this line
exclude: /\.lazy\.css$/i,
use: ["style-loader", "css-loader"],
},
// And add this rule
{
test: /\.lazy\.css$/i,
use: [
{ loader: "style-loader", options: { injectType: "lazyStyleTag" } },
"css-loader",
],
},
],
},
};
Now take your CSS files and change their name to the lazy named convention
You probably have this
styles.css
// or
styles.min.css
Now will be this:
styles.lazy.css
Then create your React theme Provider in a simple React context, this context will wrap your App so it will load the conditioned CSS everytime the context state changes. This context state is going to be availabe anywhere inside your app as well as the setter via a custom hook we will export from the same file, check this out:
import React, {
useEffect, createContext, useState, useContext,
} from 'react';
import { Nullable } from 'types';
// Import both files here like this:
// Import of CSS file number 1
import LightMode from './theme/styles.lazy.css';
// Import of CSS file number 2
import DarkMode from './theme/styles.lazy.css';
interface IContext {
theme: Nullable<string>
toggleTheme: () => void
}
const Context = createContext<IContext>({
theme: null,
toggleTheme: () => { },
});
// Your Provider component that returns the Context.Provider
// Let's also play with the sessionStorage, so this state doesn't
// brake with browser refresh or logouts
const ThemeProvider: React.FC = ({ children }) => {
// Im initialazing here the state with any existing value in the
//sessionStorage, or not...
const [theme, setTheme] = useState<Nullable<string>>(sessionStorage.getItem('themeMode') || 'dark');
// this setter Fn we can pass down to anywhere
const toggleTheme = () => {
const newThemeValue = theme === 'dark' ? 'light' : 'dark';
setTheme(newThemeValue);
sessionStorage.setItem('themeMode', newThemeValue);
};
// Now the magic, this lazy css files you can use or unuse
// This is exactly what you need, import the CSS but also unimport
// the one you had imported before. An actual toggle of import in a
// dynamic way.. brought to you by webpack
useEffect(() => {
if (theme === 'light') {
DarkMode.unuse();
LightMode.use();
} else if (theme == 'dark') {
LightMode.unuse();
DarkMode.use();
}
}, [theme]);
return (
<Context.Provider value={{ theme, toggleTheme }}>
{children}
</Context.Provider>
);
};
export default ThemeProvider;
// This useTheme hook will give you the context anywhere to set the state of // theme and this will toggle the styles imported
export const useTheme = () => useContext(Context);
Remember to put this state on the sessionStorage like in this example so your user has the state available every time it comes back or refreshes the page
Don't forget to wrap the friking App with the Provider:
import ThemeProvider from './ThemeProvider'
const App = () => {
return (
<ThemeProvider>
<App />
</ThemeProvider>
)
}
Now just toggle the CSS imports of your application using your cool useTheme hook
import { useTheme } from './yourContextFile';
// inside your component
const AnyComponentDownTheTree = () => {
const { theme, toggleTheme } = useTheme()
// use the toggleTheme function to toggle and the theme actual value
// for your components, you might need disable something or set active a
// switch, etc, etc
}
Other solution does not work for me. After one day of the search, I obtain bellow solution. In my issue, I have two CSS files for RTL or LTR like app.rtl.css or app.ltr.css
Create a functional component Style like this:
import React, { useState } from "react";
export default function Style(props) {
const [stylePath, setStylePath] = useState(props.path);
return (
<div>
<link rel="stylesheet" type="text/css" href={stylePath} />
</div>
);
}
And then you can call it, for example in App.js:
function App() {
...
return (
<Style path={`/css/app.${direction}.css`} />
)}
direction param contains rtl or ltr and determine which file should be loaded.
I tested some alternatives available in some tutorials and the best for me was to use only classes in css.
One of the problems I encountered when using
require: did not override on some occasions
import: delay generated to load css
The best way for me was to actually put a class switch
.default-sidebar {
--side-text-icon:rgba(255,255,255,.9) !important;
--side-text-section: rgb(255,255,255,.8) !important;
--side-separator-section:#ff944d !important;
}
.dark-sidebar {
--side-text-icon:rgba(255,255,255,.9) !important;
--side-text-section: rgb(255,255,255,.8) !important;
--side-separator-section:#262626 !important;
}
'
<div className={`root-sidebar ${condition?'default-sidebar':'dark-sidebar'}`}></div>

Resources