Emotion is a library for authoring and composing CSS rulesets in a performant way. Here's how to get started using it with Gatsby.
First, we'll boot up a fresh gatsby site using the default starter and install the Gatsby plugin (as well as Emotion itself).
gatsby new my-emotion-siteyarn add gatsby-plugin-emotion @emotion/core
Then we need to add gatsby-plugin-emotion
to our
gatsby-config.js
. We'll add it at the top of our plugins
list.
module.exports = {siteMetadata: {title: `Gatsby Default Starter`,description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,author: `@gatsbyjs`},plugins: [`gatsby-plugin-emotion`,`gatsby-plugin-react-helmet`,{resolve: `gatsby-source-filesystem`,options: {name: `images`,path: `${__dirname}/src/images`}},`gatsby-transformer-sharp`,`gatsby-plugin-sharp`,{resolve: `gatsby-plugin-manifest`,options: {name: `gatsby-starter-default`,short_name: `starter`,start_url: `/`,background_color: `#663399`,theme_color: `#663399`,display: `minimal-ui`,icon: `src/images/gatsby-icon.png` // This path is relative to the root of the site.}}// this (optional) plugin enables Progressive Web App + Offline functionality// To learn more, visit: https://gatsby.app/offline// 'gatsby-plugin-offline',]};
We can run yarn develop
to run our new Gatsby site. The
output will look something like this.
➜ yarn developyarn run v1.13.0$ gatsby developsuccess open and validate gatsby-configs — 0.009 ssuccess load plugins — 0.232 ssuccess onPreInit — 0.515 ssuccess delete html and css files from previous builds — 0.007 ssuccess initialize cache — 0.013 ssuccess copy gatsby files — 0.078 ssuccess onPreBootstrap — 0.008 ssuccess source and transform nodes — 0.064 ssuccess building schema — 0.333 ssuccess createPages — 0.001 ssuccess createPagesStatefully — 0.047 ssuccess onPreExtractQueries — 0.004 ssuccess update schema — 0.167 ssuccess extract queries from components — 0.137 ssuccess run graphql queries — 0.200 s — 8/8 40.32 queries/secondsuccess write out page data — 0.004 ssuccess write out redirect data — 0.001 sGenerating image thumbnails [==============================] 6/6 0.2 secs 100%info bootstrap finished - 5.413 s⡀ onPostBootstrapdone generating icons for manifestsuccess onPostBootstrap — 0.212 sDONE Compiled successfully in 4075ms 8:18:42 PMYou can now view gatsby-starter-default in the browser.http://localhost:8000/View GraphiQL, an in-browser IDE, to explore your site's data and schemahttp://localhost:8000/___graphqlNote that the development build is not optimized.To create a production build, use gatsby buildℹ 「wdm」:ℹ 「wdm」: Compiled successfully.>
The default starter comes with no external styling
implementation. There are no CSS files and no other
libraries to implement styling. If we look at our index page
at src/pages/index.js
, we see the default starter uses
inline styles.
import React from "react";import { Link } from "gatsby";import Layout from "../components/layout";import Image from "../components/image";import SEO from "../components/seo";const IndexPage = () => (<Layout><SEOtitle="Home"keywords={[`gatsby`, `application`, `react`]}/><h1>Hi people</h1><p>Welcome to your new Gatsby site.</p><p>Now go build something great.</p><divstyle={{ maxWidth: `300px`, marginBottom: `1.45rem` }}><Image /></div><Link to="/page-2/">Go to page 2</Link></Layout>);export default IndexPage;
The result of this page (specifically the div
containing
inline styles and the image) is more complex than you might
think because the image is processed into many different
resolutions automatically by Gatsby. We'll ignore most of
the output and focus on the root div
with the inline
styles. We can see that they compile to a style
attribute.
<div style="max-width: 300px; margin-bottom: 1.45rem;"><divclass=" gatsby-image-wrapper"style="position: relative; overflow: hidden;"><div style="width: 100%; padding-bottom: 100%;"></div><imgsrc="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsSAAALEgHS3X78AAACYklEQVQ4y42Uy28SQRjA+dM8efDmwYN6qF6qiSY+Y/WgQRMibY00TaWNBSRSCraYQtHl/bR0KyxQWCgWWAqU8izl/Sq7rLNsRHlVJpvJtzPfb77nDIOcZHSoqZSrp4+KtXIziubaLRysMCZiCYqOoVnhjNEi8RcztdxxeTzc6VBfT+5O2Vhpb+vw4wMdZ0ppWvP9xzLeJoDNThf2W+Nz1+XzNxQubSToSKKW+BDc+WOnkshhSVgeCiGpViZMEg1oxc26Knt+ae3bEtJTZwzE1kXLccG0+sOOlrcvZXvsczPkITfsa20vwIKnhsh+BnjUarT74Gb13CY7KBVJMv3z4N1NszQYsMWM62HNrCis/GxXn0iYls23uz5LPBcv0bH8hUH2XRoM85ySXv7JBtO87jMIvWq+H5GoYIHCLA1ZxD6Qap3Ak8IKfW7TJ50lK6uP9E6RgndHaODtCJ6Z5RyHfnE7j6gRbcKlCYNSt+rtETHTpUGgEP8FYmdNqd/Mo7aiVWTfuH2L9xASvfxxlqr01EYkrJszvNkgW9bH0OuFr+99m+y9IOeyU6zIp/Hubp/yMEztlzFPwOhdvq+nIoS1JNn4t2sugCmVsDvPe2KKolnZLCxhOcAKQRDDXTQaVi46lqYhIBwHTrl3oWqhMRDtaJge37lOBMKo4tfbqhVX0J7snTsWps8uZWuoOQY6CcjpSIF55UvmqNgr5wUwtV1IVdnXtnSfPEB2qjDNqnvczRl0m+j6Jn5lXb6nAQJqinmN0ZEBj03YLzghY8PnTRz80o/GRJZpOLCb0PM9BN7pvUEjx28V00WUg9jIVwAAAABJRU5ErkJggg=="alt=""style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 0; transition: opacity 0.5s ease 0.5s;"/><picture><sourcesrcset="/static/6d91c86c0fde632ba4cd01062fd9ccfa/0552a/gatsby-astronaut.png 75w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/fc3be/gatsby-astronaut.png 150w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/045aa/gatsby-astronaut.png 300w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/24f49/gatsby-astronaut.png 450w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/5497d/gatsby-astronaut.png 600w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/483e1/gatsby-astronaut.png 800w"sizes="(max-width: 300px) 100vw, 300px"/><imgalt=""src="/static/6d91c86c0fde632ba4cd01062fd9ccfa/045aa/gatsby-astronaut.png"style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1; transition: opacity 0.5s ease 0s;"/></picture><noscript><picture><sourcesrcset="/static/6d91c86c0fde632ba4cd01062fd9ccfa/0552a/gatsby-astronaut.png 75w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/fc3be/gatsby-astronaut.png 150w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/045aa/gatsby-astronaut.png 300w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/24f49/gatsby-astronaut.png 450w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/5497d/gatsby-astronaut.png 600w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/483e1/gatsby-astronaut.png 800w"sizes="(max-width: 300px) 100vw, 300px"/><imgsrc="/static/6d91c86c0fde632ba4cd01062fd9ccfa/045aa/gatsby-astronaut.png"alt=""style="position:absolute;top:0;left:0;transition:opacity 0.5s;transition-delay:0.5s;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/></picture></noscript></div></div>
Most people want to keep inline styles to a minimum unless
they're performing an intensive animation on one of the
values, so let's convert this to use Emotion. The only
change we need to make in this file is to swap styles
for
css
.
import React from "react";import { Link } from "gatsby";import Layout from "../components/layout";import Image from "../components/image";import SEO from "../components/seo";const IndexPage = () => (<Layout><SEOtitle="Home"keywords={[`gatsby`, `application`, `react`]}/><h1>Hi people</h1><p>Welcome to your new Gatsby site.</p><p>Now go build something great.</p><divcss={{ maxWidth: `300px`, marginBottom: `1.45rem` }}><Image /></div><Link to="/page-2/">Go to page 2</Link></Layout>);export default IndexPage;
Yeah, that's it. Let's take a look at how it renders again.
<div class="css-ehkvu9-IndexPage"><divclass=" gatsby-image-wrapper"style="position: relative; overflow: hidden;"><div style="width: 100%; padding-bottom: 100%;"></div><imgsrc="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsSAAALEgHS3X78AAACYklEQVQ4y42Uy28SQRjA+dM8efDmwYN6qF6qiSY+Y/WgQRMibY00TaWNBSRSCraYQtHl/bR0KyxQWCgWWAqU8izl/Sq7rLNsRHlVJpvJtzPfb77nDIOcZHSoqZSrp4+KtXIziubaLRysMCZiCYqOoVnhjNEi8RcztdxxeTzc6VBfT+5O2Vhpb+vw4wMdZ0ppWvP9xzLeJoDNThf2W+Nz1+XzNxQubSToSKKW+BDc+WOnkshhSVgeCiGpViZMEg1oxc26Knt+ae3bEtJTZwzE1kXLccG0+sOOlrcvZXvsczPkITfsa20vwIKnhsh+BnjUarT74Gb13CY7KBVJMv3z4N1NszQYsMWM62HNrCis/GxXn0iYls23uz5LPBcv0bH8hUH2XRoM85ySXv7JBtO87jMIvWq+H5GoYIHCLA1ZxD6Qap3Ak8IKfW7TJ50lK6uP9E6RgndHaODtCJ6Z5RyHfnE7j6gRbcKlCYNSt+rtETHTpUGgEP8FYmdNqd/Mo7aiVWTfuH2L9xASvfxxlqr01EYkrJszvNkgW9bH0OuFr+99m+y9IOeyU6zIp/Hubp/yMEztlzFPwOhdvq+nIoS1JNn4t2sugCmVsDvPe2KKolnZLCxhOcAKQRDDXTQaVi46lqYhIBwHTrl3oWqhMRDtaJge37lOBMKo4tfbqhVX0J7snTsWps8uZWuoOQY6CcjpSIF55UvmqNgr5wUwtV1IVdnXtnSfPEB2qjDNqnvczRl0m+j6Jn5lXb6nAQJqinmN0ZEBj03YLzghY8PnTRz80o/GRJZpOLCb0PM9BN7pvUEjx28V00WUg9jIVwAAAABJRU5ErkJggg=="alt=""style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 0; transition: opacity 0.5s ease 0.5s;"/><picture><sourcesrcset="/static/6d91c86c0fde632ba4cd01062fd9ccfa/0552a/gatsby-astronaut.png 75w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/fc3be/gatsby-astronaut.png 150w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/045aa/gatsby-astronaut.png 300w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/24f49/gatsby-astronaut.png 450w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/5497d/gatsby-astronaut.png 600w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/483e1/gatsby-astronaut.png 800w"sizes="(max-width: 300px) 100vw, 300px"/><imgalt=""src="/static/6d91c86c0fde632ba4cd01062fd9ccfa/045aa/gatsby-astronaut.png"naptha_cursor="text"style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1; transition: opacity 0.5s ease 0s;"/></picture><noscript><picture><sourcesrcset="/static/6d91c86c0fde632ba4cd01062fd9ccfa/0552a/gatsby-astronaut.png 75w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/fc3be/gatsby-astronaut.png 150w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/045aa/gatsby-astronaut.png 300w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/24f49/gatsby-astronaut.png 450w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/5497d/gatsby-astronaut.png 600w,/static/6d91c86c0fde632ba4cd01062fd9ccfa/483e1/gatsby-astronaut.png 800w"sizes="(max-width: 300px) 100vw, 300px"/><imgsrc="/static/6d91c86c0fde632ba4cd01062fd9ccfa/045aa/gatsby-astronaut.png"alt=""style="position:absolute;top:0;left:0;transition:opacity 0.5s;transition-delay:0.5s;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/></picture></noscript></div></div>
This time we can see that the containing div has a classname
on it instead of inline styles... but where are the styles?
Well in <head>
there's a new <style>
tag that includes
our styles and a source map (this is development mode after
all).
<style data-emotion="css">.css-ehkvu9-IndexPage {max-width: 300px;margin-bottom: 1.45rem;} /*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9wYWdlcy9pbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFhUyIsImZpbGUiOiJzcmMvcGFnZXMvaW5kZXguanMiLCJzb3VyY2VSb290IjoiL1VzZXJzL2NocmlzL3RtcC9teS1lbW90aW9uLXNpdGUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBMaW5rIH0gZnJvbSAnZ2F0c2J5J1xuXG5pbXBvcnQgTGF5b3V0IGZyb20gJy4uL2NvbXBvbmVudHMvbGF5b3V0J1xuaW1wb3J0IEltYWdlIGZyb20gJy4uL2NvbXBvbmVudHMvaW1hZ2UnXG5pbXBvcnQgU0VPIGZyb20gJy4uL2NvbXBvbmVudHMvc2VvJ1xuXG5jb25zdCBJbmRleFBhZ2UgPSAoKSA9PiAoXG4gIDxMYXlvdXQ+XG4gICAgPFNFTyB0aXRsZT1cIkhvbWVcIiBrZXl3b3Jkcz17W2BnYXRzYnlgLCBgYXBwbGljYXRpb25gLCBgcmVhY3RgXX0gLz5cbiAgICA8aDE+SGkgcGVvcGxlPC9oMT5cbiAgICA8cD5XZWxjb21lIHRvIHlvdXIgbmV3IEdhdHNieSBzaXRlLjwvcD5cbiAgICA8cD5Ob3cgZ28gYnVpbGQgc29tZXRoaW5nIGdyZWF0LjwvcD5cbiAgICA8ZGl2IGNzcz17eyBtYXhXaWR0aDogYDMwMHB4YCwgbWFyZ2luQm90dG9tOiBgMS40NXJlbWAgfX0+XG4gICAgICA8SW1hZ2UgLz5cbiAgICA8L2Rpdj5cbiAgICA8TGluayB0bz1cIi9wYWdlLTIvXCI+R28gdG8gcGFnZSAyPC9MaW5rPlxuICA8L0xheW91dD5cbilcblxuZXhwb3J0IGRlZmF1bHQgSW5kZXhQYWdlXG4iXX0= */</style>
This is how Emotion supports the full breadth of CSS which
using a syntax that looks like inline styles. It uses
built-in browser APIs (the CSSOM APIs) to insert styles in a
performant way. The syntax we used to replace the style
prop is know as the "CSS Prop", named for the css
prop we
used on our React components. In our next posts, we'll
explore more of the Emotion API, composition of styles,
theming, and how to use both the styled
and css
style
APIs.