When using createStyleSheet
, there is no way (at least that i know of) to access the component's props. I think this is important, as sometimes it is wanted to pass sizes, url images, and other stylings as props to the component.
Today the only solution to this is to separate these stuff into inline-styles, but as far as i know, material-ui v1 uses jss
and react-jss
, and those two already gives you the possibility of access the props via functions that receive the props and then return the desired style, like that:
const styles = {
button: {
background: props => props.color
},
root: {
backgroundImage: props => `url(${props.backgroundImage})`
}
}
// Reference: https://github.com/cssinjs/react-jss#example
What do you think of implementing something like that on material-ui too?
react-jss implement that feature. You can always use it over our styling solution. The real motivation for implementing that feature here should be around simpler/better internal implementation of the components.
react-jss injectStyles() does this too, but it seems to be that it would be better to add props to the StyleRuleCallback
.
const styles = (props, theme) => ({})
That way you are not limited to only values depending on props.
@Shamplol feedback makes me think that we could be taking advantage of this new feature to allow users providing a custom color
to our components, instead of the predefined one. The CSS size impact is unclear but it would make the component color override even simpler. This is something to investigate. Then, once css variables browser support is high enough (2 years from now?), we can rely on it.
@oliviertassinari wouldn't the css size actually decrease in some cases?
As i get it, we currently define all classes for ...Primary
and ...Accent
- wouldn't this change mean, that we would only have to maintain classes for ...Colorized
? Or are you concerned about the generated css?
Either way, I think this would hugely improve dx as we have to basically reimplement complex classes like https://github.com/callemall/material-ui/blob/v1-beta/src/Button/Button.js#L11 when we want to use non palette colors.
wouldn't the css size actually decrease in some cases?
@sakulstra Hard to tell. It will depend on the implementation. Maybe :).
From a TypeScript typing perspective, it would be nice if _both_ props
and theme
could be accessed this way within a styles specification:
const styles = {
button: {
background: ({ theme }) => theme.palette.primary[200]
},
root: {
backgroundImage: ({ props }) => `url(${props.backgroundImage})`
}
};
The reason is that TypeScript frequently fails to infer the right type for withStyles
when it is passed a function object, so you have to provide extra type annotations to make it work, e.g.
withStyles<'button' | 'root'>(theme => ...)
If props are passed in too, this will become
withStyles<'button' | 'root', Props>((theme, props) => ...)
What's the current status of this? It would be really nice to have that feature
@lucasmafra I have added this feature to the post v1 release milestone. The sooner we can release v1, the better.
This functionality is key to being able to write expressive style rules that combine props, media queries, and interactive states. You can't replace that functionality with inline styles. Unfortunately, withStyles
is unusable in any of my projects until this gets added.
You can always setup a custom theme, and use this convention: We really like how styled components gives you access to the props.theme out of the box by nesting ThemeProvider inside MuiThemeProvider when both called theme={theme}. It extends the default theme that mui exposes
//inline with HOC Method
h1 style= {{ 'color: this.props.theme.palette.primary[500]' }}
//styled components ( no method necessary )
const Heading = styled.h1`
color: ${p => p.theme.palette.primary['500']};
`
I just needed to use function values, so I used react-jss' injectSheet()
function. I am used to use Mui's withStyles()
function, so is there any drawback when using injectSheet instead of withStyles?
@damien-monni You don't get access to your MUI theme.
Hello all, just wanted to share my version of the Material-UI styled-components-like API. It's not "heavily" tested but it seems to work well and provides:
props
and theme
to style function. props
to wrapped componentReact.forwardRef
function
or object
prop-types, and doing checks on stateless components whether to pass through to child )Related issues: #10825, #7633
styled.js
import React, { Component } from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
function styled(WrappedComponent, customProps) {
return (style, options) => {
class StyledComponent extends Component {
render() {
let {
forwardRef,
classes,
className,
...passThroughProps
} = this.props;
return (
<WrappedComponent
ref={forwardRef}
className={classNames(classes.root, className)}
{...passThroughProps}
{...customProps}
/>
);
}
}
StyledComponent.propTypes = {
classes: PropTypes.object.isRequired,
className: PropTypes.string,
};
let hoistedProps = {};
const styles =
typeof style === 'function'
? theme => {
return { root: style({ ...theme, theme, props: hoistedProps }) };
}
: { root: style };
const WithRef = withStyles(styles, options)(StyledComponent);
return React.forwardRef((props, ref) => {
hoistedProps = props;
return <WithRef {...props} forwardRef={ref} />;
});
};
}
export default styled;
Example use
const MobileNav = styled('nav')(({ theme, props }) => ({
'&.pos-fixed': {
top: 0,
position: props.fixed ? 'fixed' : 'absolute',
zIndex: 99,
animation: 'fadeInDown 0.3s ease-out forwards',
backgroundColor: theme.palette.common.white,
},
}));
With custom props
const StyledButton = styled(Button, { component: Link })(
({ theme, props }) => ({
color: props.disabled ? 'gray' : 'blue',
[theme.breakpoints.down('sm')]: {
color: 'red',
},
})
);
@oliviertassinari To solve this problem, I'm using the solution below:
componentWillMount() {
const {color} = this.props;
jss.setup(jssPreset());
const stylesheet = jss.createStyleSheet({
underline: {
"&:after": {
backgroundColor: `${color} !important`
}
}
}).attach();
this.underlineClass = stylesheet.classes.underline;
}
It works great but are there some potential issues I'm not seeing? I don't like that I have to call jss.setup twice for instance 馃槄. I'm not sure I understand jss
lifecycle. I was surprised that I needed to invoke setup()
here.
It works great but are there some potential issues I'm not seeing?
@wcandillon Some potential issue I can see: 1. You will inject new class names everytime an instance of the component is mounted. 2. You won't be able to server-side render your component. 3. You won't get the correct CSS override injection order.
I developped this library to have props with style:
https://github.com/JacquesBonet/jss-material-ui
Tested successfully on a project.
At first, I use danielmahon solution, but get style inheritance problem.
@oliviertassinari Do we have an alternative approach for creating dynamic css / animations at the moment? Thanks :)
@HunderlineK Some alternatives: https://material-ui.com/customization/overrides/#2-dynamic-variation-for-a-one-time-situation.
@danielmahon your approach is just what I'm looking for right now to solve my problem, though the "Styled Component" does not re render when it receive new props. Have you tried anything else?
I'll think of something different, and if I come up with something I'll let you know
馃挵 Just attached a $50 bounty to that one :)
Like @lewisdiamond said, const styles = (props, theme) => ({})
would be really neat.
Or const styles = (theme, props) => ({})
to be non-breaking maybe.
I'm adding this issue in the v1.x milestone. A lot of people are requesting it. We will soon get to it.
@oliviertassinari this probably has significant implications for the typing of withStyles
, let me know if I can help with that
@pelotom Thanks, I will let you know. I hope I can start looking at the issue this month.
Is there work in progress regarding this? It's a key feature IMO, maybe I could help with it.
EDIT: I started to work on it. Managed to pass props to the styles
function that withStyles
accepts, only problem is styles are not updated when props change. Will create a PR when that is solved.
Hi I just came across a use case where i needed this to customize the colors of the avatar component and there is no other way to control the style for all the variants of the component other than this.
const styles = theme => ({
chip:{
},
separator: {
marginRight: theme.spacing.unit,
},
fright: {
float: 'right',
},
fleft: {
float: 'left',
},
outlinedPrimary:{
color: props => stringToColor( props.username),
border: props => `1px solid ${fade(stringToColor(props.username), 0.5)}`,
'$clickable&:hover, $clickable&:focus, $deletable&:focus': props => ({
backgroundColor: fade(stringToColor(props.username), theme.palette.action.hoverOpacity),
border: `1px solid ${stringToColor(props.username)}`,
}),
},
outlined: {
backgroundColor: 'transparent',
border: props => `1px solid ${
theme.palette.type === 'light' ? stringToColor(props.username) : fade(stringToColor(props.username))
}`,
'$clickable&:hover, $clickable&:focus, $deletable&:focus': props => ({
backgroundColor: fade(stringToColor(props.username), theme.palette.action.hoverOpacity),
}),
},
});
You can check my solution Japrogramer: https://github.com/JacquesBonet/jss-material-ui
thanks, I will take a look at it.
I needed this feature earlier today so i wrote a HOC. withStyles
does some caching on its own so i can't really tell how much this affect that but i will look at the caching implementation of withStyles
in my spare time, for now anyone looking for a quick way to get props and theme to play nice there you go
This component will do a full remount if props change something to do with the index number of the stylesheet class changing or somthing in the withStyles HOC
import React from 'react';
import { withStyles } from '@material-ui/core/styles';
const { createElement, forwardRef } = React
const withPropsStyles = ( style ) => {
const withPropsStyles = ( component ) => {
return forwardRef( (props, ref) => {
const proxy = (theme) => style(props, theme)
const hoc = withStyles(proxy)(component)
return props.children ?
createElement(hoc, { ...props, ref}, props.children) :
createElement(hoc, {...props, ref})
})
}
return withPropsStyles
}
export default withPropsStyles
const styles = (props, theme) => ({
root:{
backgroundColor: props.light ? theme.palette.primary.light : theme.palette.primary.main
},
})
const SomeComponent = ({classes}) => <div className={classes.root}/>
export default withPropsStyles(styles)(SomeComponent)
Simple and works(but rem the full remount cost too)
With this change can we remove inline style usage from the library in favor of 100% JSS? My app does not work with inline styles and when I went to replace the ripple effect with JSS I realized I needed this feature. Maybe a small performance hit, but seems cleaner.
@koshea ditch the inline style. I also really don't like inline styles too, it should work just fine as a drop in replacement for withStyles
as a decorator or as in the example.
I also wanted to mention, that using inline styles doesn't allow to enable strong Content Security Policy.
It requires adding unsafe-inline
flag to styles directives, which is not secure.
Dynamic styles with props support should resolve that problem
Hi guys, sorry to jump in to the discussion "just like this". I recently started using Mui (with Typescript) and whilst I find it an extremely powerful library, it certainly has its complexities.
I noticed in some comments above that there's a bit of a discussion for whether this feature should be (props, theme) => {}
or (theme, props) => {}
. I'd like to reinforce what @pelotom said about making both props
and theme
named properties in his comment. Making it that way will probably make it easier for us to refactor the style definitions once this change lands (which I'm really looking forward to). Cheers 馃檪
Thank you everyone for the patience! This issue is being taken care of in #13503. It's a requirement for the component helpers we want to implement. We have also started experimenting with the hook API: https://twitter.com/olivtassinari/status/1058805751404261376.
Did this make it into 4.0? It looks like the makeStyles
callback does not have a props
parameter.
@city41 const classes = useStyles(props);
I see. So it looks like it is
const useStyles = makeStyles(theme => {
return {
foo: {
color: theme.props.danger ? '#ff0000' : '#00ff00'
}
};
});
function MyComponent(props) {
const classes = useStyles(props);
return <div className={classes.foo}>...</div>;
}
I don't see this documented in the styles API section on the website. Let me see if I can send a PR.
@city41 There is a start of a documentation in https://material-ui.com/styles/basics/#adapting-based-on-props.
cool, glad to see the docs are getting updated. for anyone else coming to this issue, here is how I combined theme and props to style a component
import React from 'react';
import { Button, Theme, makeStyles } from '@material-ui/core';
interface ButtonProps {
destructive: boolean;
}
const useButtonStyles = makeStyles<Theme, ButtonProps>(theme => {
return {
root: props => ({
backgroundColor: props.destructive ? theme.palette.error.main : theme.palette.primary.main
})
};
});
export const PrimaryButton: React.FunctionComponent<ButtonProps> = props => {
const classes = useButtonStyles(props);
return <Button {...props} className={classes.root} variant="contained" />;
};
How can i use prop in an external styles json file ?
for example this is external file
const typographyStyle = {
title2: {
fontFamily:"Montserrat",
fontStyle:"normal",
fontWeight:"800",
fontSize:"72px",
lineHeight:"88px",
letterSpacing:"-0.02em",
// color:"#304C82"
color : props => {
console.log(props,'c1');
return props.color
}
}
};
export default typographyStyle;
i import this file and spread the object
import typographyStyle from "../assets/jss/material-kit-pro-react/views/componentsSections/typographyStyle";
const styles = theme => ({
...typographyStyle,
homeSearch:{
width: '100%',
'& div':{
'& input':{
"color":"#304C82",
height: 65,
fontFamily: 'Open Sans',
fontStyle: 'normal',
fontWeight: 800,
fontSize: '48px',
lineHeight: '65px',
letterSpacing: '-0.03em',
'&::placeholder':{
fontFamily: 'Open Sans',
fontStyle: 'normal',
fontWeight: 800,
fontSize: '48px',
lineHeight: '65px',
letterSpacing: '-0.03em',
color: '#EAEAEA'
}
}
}
},
});
now on color function i get props = {} .
can someone help me in this regard ?
UPDATE:
seems like i am doing something wrong cause i am getting empty object even in my main styles.js file
homeSearch: props => {
console.log(props);
return {
width: '100%',
border: `1px solid ${props.color}`
,
'& div':{
'& input':{
"color":"#304C82",
height: 65,
fontFamily: 'Open Sans',
fontStyle: 'normal',
fontWeight: 800,
fontSize: '48px',
lineHeight: '65px',
letterSpacing: '-0.03em',
'&::placeholder':{
fontFamily: 'Open Sans',
fontStyle: 'normal',
fontWeight: 800,
fontSize: '48px',
lineHeight: '65px',
letterSpacing: '-0.03em',
color: '#EAEAEA'
}
}
}
}
},
Most helpful comment
I'm adding this issue in the v1.x milestone. A lot of people are requesting it. We will soon get to it.