CSS in JS
You can leverage our styling solution, even if you are not using our components.
Material-UI aims to provide strong foundations for building dynamic UIs. For the sake of simplicity, we expose our styling solution to users. You can use it, but you don't have to. This styling solution is interoperable with all the other major solutions.
Material-UI's styling solution
In previous versions, Material-UI has used LESS, then a custom inline-style solution to write the style of the components, but these approaches have proven to be limited. Most recently, we have moved toward a CSS-in-JS solution. It unlocks many great features (theme nesting, dynamic styles, self-support, etc.). We think that it's the future:
So, you may have noticed in the demos what CSS-in-JS looks like.
We use the higher-order component created by withStyles
to inject an array of styles into the DOM as CSS, using JSS. Here's an example:
JSS
Material-UI's styling solution uses JSS at its core. It's a high performance JS to CSS compiler which works at runtime and server-side. It is about 8 kB (minified and gzipped) and is extensible via a plugins API.
If you end up using this styling solution in your codebase, you're going to need to learn the API.
The best place to start is by looking at the features that each plugin provides. Material-UI uses few of them.
You can always add new plugins if needed with the JssProvider
helper.
If you wish to build your own instance of jss
and support rtl make sure you also include the jss-rtl plugin.
Check the jss-rtl readme to learn how.
Sheets registry
When rendering on the server, you will need to get all rendered styles as a CSS string.
The SheetsRegistry
class allows you to manually aggregate and stringify them.
Read more about Server Rendering.
Sheets manager
The sheets manager uses a reference counting algorithm in order to attach and detach the style sheets only once per (styles, theme) couple. This technique provides an important performance boost when re-rendering instances of a component.
When only rendering on the client, that's not something you need to be aware of. However, when rendering on the server you do. You can read more about Server Rendering.
Class names
You may have noticed that the class names generated by our styling solution are non-deterministic, so you can't rely on them to stay the same. The following CSS won't work:
.MuiAppBar-root-12 {
opacity: 0.6
}
Instead, you have to use the classes
property of a component to override them.
On the other hand, thanks to the non-deterministic nature of our class names, we
can implement optimizations for development and production.
They are easy to debug in development and as short as possible in production:
- development:
.MuiAppBar-root-12
- production:
.jss12
If you don't like this default behavior, you can change it. JSS relies on the concept of class name generator.
Global CSS
We provide a custom implementation of the class name generator for Material-UI needs:
createGenerateClassName()
.
As well as the option to make the class names deterministic with the dangerouslyUseGlobalCSS
option. When turned on, the class names will look like this:
- development:
.MuiAppBar-root
- production:
.MuiAppBar-root
⚠️ Be cautious when using dangerouslyUseGlobalCSS
.
We provide this option as an escape hatch for quick prototyping.
Relying on it for code running in production has the following implications:
- Global CSS is inherently fragile. People use strict methodologies like BEM to workaround the issue.
- It's harder to keep track of
classes
API changes.
⚠️ When using dangerouslyUseGlobalCSS
standalone (without Material-UI), you should name your style sheets. withStyles
has a name option for that:
const Button = withStyles(styles, { name: 'button' })(ButtonBase)
CSS injection order
The CSS injected by Material-UI to style a component has the highest specificity possible as the <link>
is injected at the bottom of the <head>
to ensure the components always render correctly.
You might, however, also want to override these styles, for example with styled-components.
If you are experiencing a CSS injection order issue, JSS provides a mechanism to handle this situation.
By adjusting the placement of the insertionPoint
within your HTML head you can control the order that the CSS rules are applied to your components.
HTML comment
The simplest approach is to add an HTML comment that determines where JSS will inject the styles:
<head>
<!-- jss-insertion-point -->
<link href="..." />
</head>
import JssProvider from 'react-jss/lib/JssProvider';
import { create } from 'jss';
import { createGenerateClassName, jssPreset } from '@material-ui/core/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 = 'jss-insertion-point';
function App() {
return (
<JssProvider jss={jss} generateClassName={generateClassName}>
...
</JssProvider>
);
}
export default App;
Other HTML element
Create React App strips HTML comments when creating the production build. To get around the issue, you can provide a DOM element (other than a comment) as the JSS insertion point.
For example, a <noscript>
element:
<head>
<noscript id="jss-insertion-point"></noscript>
<link href="..." />
</head>
import JssProvider from 'react-jss/lib/JssProvider';
import { create } from 'jss';
import { createGenerateClassName, jssPreset } from '@material-ui/core/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;
JS createComment
codesandbox.io prevents the access to the <head>
element.
To get around the issue, you can use the JavaScript document.createComment()
API:
import JssProvider from 'react-jss/lib/JssProvider';
import { create } from 'jss';
import { createGenerateClassName, jssPreset } from '@material-ui/core/styles';
const styleNode = document.createComment("jss-insertion-point");
document.head.insertBefore(styleNode, document.head.firstChild);
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 = 'jss-insertion-point';
function App() {
return (
<JssProvider jss={jss} generateClassName={generateClassName}>
...
</JssProvider>
);
}
export default App;
JssProvider
react-jss exposes a JssProvider
component to configure JSS for the underlying child components.
There are different use cases:
- Providing a class name generator.
- Providing a Sheets registry.
- Providing a JSS instance. You might want to support Right-to-left or changing the CSS injection order. Read the JSS documentation to learn more about the options available. Here is an example:
import JssProvider from 'react-jss/lib/JssProvider';
import { create } from 'jss';
import { createGenerateClassName, jssPreset } from '@material-ui/core/styles';
const generateClassName = createGenerateClassName();
const jss = create(jssPreset());
function App() {
return (
<JssProvider jss={jss} generateClassName={generateClassName}>
...
</JssProvider>
);
}
export default App;
Plugins
JSS uses the concept of plugins to extend its core, allowing people to cherry-pick the features they need.
You pay the performance overhead for only what's you are using.
Given withStyles
is our internal styling solution, all the plugins aren't available by default. We have added the following list:
It's a subset of jss-preset-default.
Of course, you are free to add a new plugin. We have one example for the jss-rtl
plugin.
API
withStyles(styles, [options]) => higher-order component
Link a style sheet with a component.
It does not modify the component passed to it; instead, it returns a new component with a classes
property.
This classes
object contains the name of the class names injected in the DOM.
Some implementation details that might be interesting to being aware of:
- It adds a
classes
property so you can override the injected class names from the outside. - It adds an
innerRef
property so you can get a reference to the wrapped component. The usage ofinnerRef
is identical toref
. - It forwards non React static properties so this HOC is more "transparent".
For instance, it can be used to defined a
getInitialProps()
static method (next.js).
Arguments
styles
(Function | Object): A function generating the styles or a styles object. It will be linked to the component. Use the function signature if you need to have access to the theme. It's provided as the first argument.options
(Object [optional]):options.withTheme
(Boolean [optional]): Defaults tofalse
. Provide thetheme
object to the component as a property.options.name
(String [optional]): The name of the style sheet. Useful for debugging. If the value isn't provided, it will try to fallback to the name of the component.options.flip
(Boolean [optional]): When set tofalse
, this sheet will opt-out thertl
transformation. When set totrue
, the styles are inversed. When set tonull
, it followstheme.direction
.- The other keys are forwarded to the options argument of jss.createStyleSheet([styles], [options]).
Returns
higher-order component
: Should be used to wrap a component.
Examples
import { withStyles } from '@material-ui/core/styles';
const styles = {
root: {
backgroundColor: 'red',
},
};
class MyComponent extends React.Component {
render () {
return <div className={this.props.classes.root} />;
}
}
export default withStyles(styles)(MyComponent);
Also, you can use as decorators like so:
import { withStyles } from '@material-ui/core/styles';
const styles = {
root: {
backgroundColor: 'red',
},
};
@withStyles(styles)
class MyComponent extends React.Component {
render () {
return <div className={this.props.classes.root} />;
}
}
export default MyComponent
createGenerateClassName([options]) => class name generator
A function which returns a class name generator function.
Arguments
options
(Object [optional]):options.dangerouslyUseGlobalCSS
(Boolean [optional]): Defaults tofalse
. Makes the Material-UI class names deterministic.options.productionPrefix
(String [optional]): Defaults to'jss'
. The string used to prefix the class names in production.
Returns
class name generator
: The generator should be provided to JSS.
Examples
import JssProvider from 'react-jss/lib/JssProvider';
import { createGenerateClassName } from '@material-ui/core/styles';
const generateClassName = createGenerateClassName({
dangerouslyUseGlobalCSS: true,
productionPrefix: 'c',
});
function App() {
return (
<JssProvider generateClassName={generateClassName}>
...
</JssProvider>
);
}
export default App;
Alternative APIs
Do you think that higher-order components are the new mixins? Rest assured we don't, however because withStyles()
is a higher-order component, it can be extended with just a few lines of code to match different patterns that are all idiomatic React. Here are a couple of examples.
Render props API (+11 lines)
The term “render prop” refers to a simple technique for sharing code between React components using a prop whose value is a function.
// You will find the `createStyled` implementation in the source of the demo.
const Styled = createStyled({
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, .3)',
},
});
function RenderProps() {
return (
<Styled>
{({ classes }) => (
<Button className={classes.root}>
{'Render props'}
</Button>
)}
</Styled>
);
}
You can access the theme the same way you would do it with withStyles
:
const Styled = createStyled(theme => ({
root: {
backgroundColor: theme.palette.background.paper,
},
}));
@jedwards1211 Has taken the time to move this module into a package: material-ui-render-props-styles. Feel free to use it.
styled-components API (+15 lines)
styled-components's API removes the mapping between components and styles. Using components as a low-level styling construct can be simpler.
// You will find the `styled` implementation in the source of the demo.
// You can even write CSS with https://github.com/cssinjs/jss-template.
const MyButton = styled(Button)({
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, .3)',
});
function StyledComponents() {
return <MyButton>{'Styled Components'}</MyButton>;
}
You can access the theme the same way you would do it with withStyles
:
const MyButton = styled(Button)(theme => ({
backgroundColor: theme.palette.background.paper,
}));