DEV Community

Andrew Welch
Andrew Welch

Posted on • Originally published at nystudio107.com on

2

Using Tailwind CSS with Gatsby, React & Emotion Styled Components

Using Tailwind CSS with Gatsby, React & Emotion Styled Components

Learn how to use the util­i­ty-first Tail­wind CSS with Emo­tion ​“CSS-in-JS” Styled Com­po­nents in a Gats­by JS + React project.

Andrew Welch / nystudio107

Gatsby react emotion styled components

Tail­wind CSS is a util­i­ty-first CSS frame­work that allows for rapid­ly build­ing cus­tom designs, and it is some­thing I’ve adopt­ed as a stan­dard part of my workflow.

If you want to hear the why’s and how’s of Tail­wind CSS, check out the Tail­wind CSS Util­i­ty-First CSS dev​Mode​.fm pod­cast episode. For the pur­pos­es of this arti­cle, we’ll just assume you’re all on-board the Tail­wind Express like I am.

Tailwind utility first css express

Since I’m a fan of Tail­wind CSS, when I start­ed work­ing with Gats­by & React, I want­ed a way to bring Tail­wind with me. I end­ed up decid­ing to go with the Emo­tion CSS-in-JS approach, specif­i­cal­ly using Styled Com­po­nents.

Again, we’ll just assume you’re on-board with Emo­tion CSS-in-JS; if not, check out the CSS in JS, an Emo­tion­al Top­ic dev​Mode​.fm pod­cast episode.

This arti­cle dis­cuss­es how to make all of these tech­nolo­gies play nice togeth­er, and explores some of the fun things the result­ing stack enables you to do.

If you want a head start on this set­up, check out Paulo Elias’s gats­by-tail­wind-emo­tion-starter to get you going. Oth­er­wise, buck­le up and let’s dive right in!

Why are we doing this?

If you haven’t worked with CSS-in-JS or Styled Com­po­nents before, there are some nice advantages:

  • You can use full-blown JavaScript
  • CSS is scoped to just the com­po­nent, and does­n’t ​“bleed out”
  • You auto­mat­i­cal­ly get just the CSS used on each page
  • You auto­mat­i­cal­ly get Crit­i­cal CSS
  • The end result is just CSS

You may or may not be con­vinced that CSS-in-JS is a good idea, but these are tan­gi­ble ben­e­fits that I’ve found, and thus my desire to use CSS-in-JS and Styled Components.

Adding Tail­wind CSS to the mix brings all of the won­der­ful ben­e­fits of a utilty-first CSS frame­work like Tail­wind CSS to our Styled Components.

So with that said, let’s get to it!

A Hybrid Approach

The excel­lent Gats­by doc­u­men­ta­tion has two approach­es list­ed for using Tail­wind CSS with Gatsby:

We’re actu­al­ly going to use a hybrid approach, using both the tailwind.macro Babel Macro and PostC­SS.

We’re going to use the tailwind.macro to trans­late Tail­wind CSS class­es to Emo­tion Styled Com­po­nents for us, gen­er­at­ing just the CSS selec­tors we actu­al­ly use.

Then we’ll use PostC­SS for build­ing the Tail­wind CSS base styles (and any base glob­al styles we want to use) that will be applied glob­al­ly to every page. This gives us things like the Nor­mal­ize CSS reset as a base.

This is the rea­son for the hybrid approach: if we just used the tailwind.macro, we would­n’t get any of the Tail­wind CSS base styles.

You could also use the gats­by-theme-tail­wind­c­ss Gats­by Theme as a way to scaf­fold things, but I think it makes sense to under­stand how all of the pieces fit togeth­er first.

So let’s get going by installing Tail­wind CSS:


# Using npm
npm install --save tailwindcss

# Using Yarn
yarn add tailwindcss

Next up, let’s get tailwind.macro set up!

Set­ting up tailwind.macro

The tailwind.macro is a Babel Macro that allows you to use Tail­wind CSS with any CSS-in-JS library. We’ve cho­sen to use Emo­tion, so let’s get it all installed:


# Using npm
npm install --save @emotion/core @emotion/styled gatsby-plugin-emotion tailwind.macro@next

# Using Yarn
yarn add @emotion/core @emotion/styled gatsby-plugin-emotion tailwind.macro@next

Then we need to add the gatsby-plugin-emotion to our gatsby-config.js (in the project root):


module.exports = {
  plugins: [
    {
      resolve: `gatsby-plugin-emotion`,
      options: {
        // Accepts all options defined by `babel-plugin-emotion` plugin.
      },
    },
  ],
}

Then we need to cre­ate a babel-plugin-macros-config.js to tell it that we want to use Emo­tion, and where our tailwind.config.js file lives:


module.exports = {
    tailwind: {
        styled: '@emotion/styled',
        config: './tailwind.config.js',
        format: 'auto',
    }
};

Final­ly, there’s a small issue we need to work around, which appears to be due to Tail­wind CSS’s use of reduce-css-calc start­ing with Tailwind ^1.1.0, which appar­ent­ly depends on it being run via Node.

We can work around this by adding the fol­low­ing to our gatsby-node.js file (in the project root):


exports.onCreateWebpackConfig = ({actions, getConfig}) => {
    // Hack due to Tailwind ^1.1.0 using `reduce-css-calc` which assumes node
    // https://github.com/bradlc/babel-plugin-tailwind-components/issues/39#issuecomment-526892633
    const config = getConfig();
    config.node = {
        fs: 'empty'
    };
};

Using tailwind.macro

Now that we’ve got tailwind.macro installed, let’s have a look at how we can use it. It works very sim­i­lar to Emo­tion Styled Com­po­nents:


import React from 'react';
import tw from 'tailwind.macro';

const PageContainer = tw.div`
    bg-gray-200 text-xl w-1/2
`;

const Layout = ({children}) => (
    <PageContainer>
        {children}
    </PageContainer>
);

export default Layout;

This will cre­ate a Styled Com­po­nent that is com­posed of the styles from the Tail­wind CSS class­es list­ed in the tem­plate lit­er­al. There’s a Github issue that shows a good exam­ple of how this works.

In devel­op­ment :


import tw from 'tailwind.macro'
let styles = tw`w-1/2`

// ↓↓↓↓↓↓↓↓

import _tailwind from './path/to/your/tailwind.js'
let styles = {
  width: _tailwind.widths['1/2']
}

In pro­duc­tion (NODE_ENV=production):


import tw from 'tailwind.macro'
let styles = tw`w-1/2`

// ↓↓↓↓↓↓↓↓

let styles = {
  width: '50%'
}

You can see how it direct­ly uses the JavaScript Tail­wind CSS con­fig to extract styles. The only real down­side to this approach is that it will not work with Tail­wind Plu­g­ins.

Build­ing on the ini­tial exam­ple above, we can mix and match Emo­tion Styled Com­po­nents cus­tom CSS with Tail­wind class­es easily:


import React from 'react';
import styled from '@emotion/styled';
import tw from 'tailwind.macro';

import background from '../../static/fabric_plaid@2x.png';

const PageContainer = styled.div`
    ${tw`
        bg-gray-200 text-xl w-1/2
    `}
    background-image: url(${background});
    padding: 10px;
`;

const Layout = ({children}) => (
    <PageContainer>
        {children}
    </PageContainer>
);

export default Layout;

So it actu­al­ly com­bines the CSS styles from the Tail­wind CSS class­es in the ${tw` tem­plate lit­er­al with the cus­tom styled com­po­nent CSS below it. So you can do any of the fun pat­terns you do with Emo­tion Styled Com­po­nents, too!

In either case, only the CSS that is actu­al­ly used on a page will be extract­ed (no need for PurgeC­SS), and the styles used by com­po­nents on a page will be inlined, appear­ing some­thing like this:

`

.css-14xcvvf-PageContainer {
    background-color:#edf2f7;
    font-size:1.25rem;
    width:50%;
    background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQBAMAAABykSv/AAAAG1BMVEXq6urr6+vp6eno6Ojn5+fs7Ozt7e3m5ubu7u7dMibkAAAf6ElEQVR4AdyX4W3jSgyEp4WvhWmBLaiFaWFbeC2k7AfNSoICHHC4vyISO8vlfOQAdGxL2PaMx);
    padding:10px;
}

`

The hashed CSS class name ensures that the styles are scoped to just the com­po­nent they are applied to, and will not leak out and affect any­thing else.

Sweet!

Set­ting up PostCSS

If we just left things as-is, using only the tailwind.macro, every­thing would still work, but we would­n’t get the Tail­wind CSS base styles to do things like apply a Nor­mal­ize CSS style reset, and oth­er glob­al styles.

Because these base styles are super use­ful, we’ll con­fig­ure PostC­SS to gen­er­ate them for us. So let’s install the PostC­SS pack­ages we’re going to use:

`

Using npm

npm install --save gatsby-plugin-postcss postcss-import postcss-preset-env stylelint

Using Yarn

yarn add gatsby-plugin-postcss postcss-import postcss-preset-env stylelint

`

Then we need to add the gatsby-plugin-postcss to our gatsby-config.js (in the project root):

`

module.exports = {
plugins: [
{
resolve: gatsby-plugin-emotion,
options: {
// Accepts all options defined by babel-plugin-emotion plugin.
},
},
{
resolve: gatsby-plugin-postcss,
options: {
// Accepts all options defined by gatsby-plugin-postcss plugin.
},
},
],
}

`

We’ll also need to cre­ate a postcss.config.js file (in the project root) to tell it what PostC­SS plu­g­ins we want to use (includ­ing tailwindcss):

`

module.exports = {
plugins: [
require('postcss-import')({
plugins: [
require('stylelint')
]
}),
require('tailwindcss')('./tailwind.config.js'),
require('postcss-preset-env')({
autoprefixer: { grid: true },
features: {
'nesting-rules': true
},
browsers: [
'> 1%',
'last 2 versions',
'Firefox ESR',
]
})
]
};

`

The postcss.config.js file is used by PostC­SS to con­fig­ure the plu­g­ins and set­tings that PostC­SS will use. The impor­tant bit here is that we’re doing a require('tailwindcss') to include Tail­wind CSS.

The oth­er PostC­SS plu­g­ins we’re includ­ing here are just things that I find use­ful. You can read more about them in-depth in the An Anno­tat­ed web­pack 4 Con­fig for Fron­tend Web Devel­op­ment article.

Then we need to cre­ate a file for the Tail­wind CSS base styles, as well as any glob­al styles we might want to add in src/utils/globals.css:

`

@tailwind base;

// Add any global styles here

`

Final­ly, we need to load these styles in the gatsby-browser.js (in the project root):

`

import "./src/utils/globals.css"

`

That’s it! When we do a build, it’ll now gen­er­ate the Tail­wind CSS base styles, and any of our glob­al styles, and include them on the page. Here’s a trun­cat­ed ver­sion of what that looks like:

`

/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */

html {
line-height: 1.15;
-webkit-text-size-adjust: 100%
}

body {
margin: 0
}

main {
display: block
}

/* Truncated here */

`

Wrap­ping Up

That’s all she wrote! I’ve found that being able to inter­twine the famil­iar Tail­wind CSS styles that I’m used to with Emo­tion Styled Com­po­nents had cre­at­ed a real­ly com­pelling way to work with CSS.

In the process of actu­al­ly using CSS-in-JS and Styled Com­po­nents, I found that many of my uncer­tain feel­ings about it dis­ap­peared. It turned my frown upside down.

Css in js emotions

Part of the rea­son is that all of this is just a real­ly sophis­ti­cat­ed way to gen­er­ate and scope CSS in a way that allows for an excel­lent devel­op­er experience.

And since we’re using Gats­by to ren­der every­thing out to sta­t­ic pages, it results in an excel­lent user expe­ri­ence too.

The fact that it ends up scop­ing the CSS to each com­po­nent, and auto­mat­i­cal­ly inlin­ing just the CSS that we use on each page is pret­ty fantastic.

It makes process­es like PurgeC­SS and Crit­i­cal CSS as described in the Imple­ment­ing Crit­i­cal CSS on your web­site arti­cle unnecessary.

Give it a whirl, and you might just sec­ond that emo­tion.

Further Reading

If you want to be notified about new articles, follow nystudio107 on Twitter.

Copyright ©2020 nystudio107. Designed by nystudio107

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)