title |
---|
The css Prop |
The primary way to style elements with emotion is the css
prop. It provides a concise and flexible API to style your components.
There are 2 ways to get started with the css
prop.
Both methods result in the same compiled code.
After adding the preset or setting the pragma as a comment, compiled jsx code will use emotion's jsx
function instead of React.createElement
.
Input | Output | |
---|---|---|
Before | <img src="avatar.png" /> |
React.createElement('img', { src: 'avatar.png' }) |
After | <img src="avatar.png" /> |
jsx('img', { src: 'avatar.png' }) |
This method will not work with Create React App or other projects that do not allow custom Babel configurations. Use the JSX Pragma method instead.
.babelrc
{
"presets": ["@emotion/babel-preset-css-prop"]
}
If you are using the compatible React version (>=16.14.0
) then you can opt into using the new JSX runtimes by using such configuration:
.babelrc
{
"presets": [
[
"@babel/preset-react",
{ "runtime": "automatic", "importSource": "@emotion/react" }
]
],
"plugins": ["@emotion/babel-plugin"]
}
In case you want to use the new JSX runtimes with Next.js and you are using their next/babel
preset then the configuration should look like this:
.babelrc
{
"presets": [
[
"next/babel",
{
"preset-react": {
"runtime": "automatic",
"importSource": "@emotion/react"
}
}
]
],
"plugins": ["@emotion/babel-plugin"]
}
Set the jsx pragma at the top of your source file that uses the css
prop.
This option works best for testing out the css
prop feature or in projects where the babel configuration is not configurable (create-react-app, codesandbox, etc.).
/** @jsx jsx */
Similar to a comment containing linter configuration, this configures the jsx babel plugin to use the jsx
function instead of React.createElement
.
If you are using a zero-config tool with automatic detection of which runtime (classic vs. automatic) should be used and you are already using a React version that has the new JSX runtimes (hence runtime: 'automatic'
being configured automatically for you) such as Create React App 4 then /** @jsx jsx */
pragma might not work and you should use /** @jsxImportSource @emotion/react */
instead.
/** @jsx jsx */
import { jsx } from '@emotion/react'
Note that excluding this will cause your css to render as [Object Object]
.
Any component or element that accepts a className
prop can also use the css
prop. The styles supplied to the css
prop are evaluated and the computed class name is applied to the className
prop.
The css
prop accepts object styles directly and does not require an additional import.
// @live
render(
<div
css={{
backgroundColor: 'hotpink',
'&:hover': {
color: 'lightgreen'
}
}}
>
This has a hotpink background.
</div>
)
To pass string styles, you must use css
which is exported by @emotion/react
, it can be used as a tagged template literal like below.
// @live
import { css } from '@emotion/react'
const color = 'darkgreen'
render(
<div
css={css`
background-color: hotpink;
&:hover {
color: ${color};
}
`}
>
This has a hotpink background.
</div>
)
Note:
css
from@emotion/react
does not return the computed class name string. The function returns an object containing the computed name and flattened styles. The returned object is understood by emotion at a low level and can be composed with other emotion based styles inside of thecss
prop, othercss
calls, or thestyled
API.
You can also pass in your css as variables, which allows for composition (read more about this here).
- Class names containing emotion styles from the
className
prop overridecss
prop styles. - Class names from sources other than emotion are ignored and appended to the computed emotion class name.
The precedence order may seem counter-intuitive, but it allows components with styles defined on the css
prop to be customized via the className
prop passed from the parent.
The P
component in this example has its default styles overridden in the ArticleText
component.
const P = props => (
<p
css={{
margin: 0,
fontSize: 12,
lineHeight: '1.5',
fontFamily: 'Sans-Serif',
color: 'black'
}}
{...props} // <- props contains the `className` prop
/>
)
const ArticleText = props => (
<P
css={{
fontSize: 14,
fontFamily: 'Georgia, serif',
color: 'darkgray'
}}
{...props} // <- props contains the `className` prop
/>
)
The ArticleText
component can be customized and the styles composed with its default styles. The result is passed P
and the process repeats.
const P = props => (
<p
css={{
margin: 0,
fontSize: 12,
lineHeight: '1.5',
fontFamily: 'sans-serif',
color: 'black'
}}
{...props} // <- props contains the `className` prop
/>
)
const ArticleText = props => (
<P
css={{
fontSize: 14,
fontFamily: 'Georgia, serif',
color: 'darkgray'
}}
{...props} // <- props contains the `className` prop
/>
)
const SmallArticleText = props => (
<ArticleText
css={{
fontSize: 10
}}
{...props} // <- props contains the `className` prop
/>
)
The styles are concatenated together and inserted via insertRule
.
P
component
.css-1 {
margin: 0;
font-size: 12px;
line-height: 1.5;
font-family: sans-serif;
color: black;
}
ArticleText
component
.css-2 {
font-size: 14px;
font-family: Georgia, serif;
color: darkgray;
}
SmallArticleText
component
.css-3 {
font-size: 10px;
}
- Result
.css-result {
+ margin: 0;
- font-size: 12px;
+ line-height: 1.5;
- font-family: 'sans-serif';
- color: black;
- font-size: 14px;
+ font-family: Georgia, serif;
+ color: darkgray;
+ font-size: 10px;
}
Relying on the css spec's "Order of Appearance" rule, property values defined later (green) override those before it (red).
- If you include the plugin
@babel/plugin-transform-react-inline-elements
in your.babelrc
your styles will not be applied. The plugin is not compatible with thecss
prop. - When using
React.cloneElement
you can't easily add a css prop if the cloned element doesn't already have it. OtherwiseReact.cloneElement
works withjsx
-created elements.