Globally Style the Gatsby Default Starter with styled-components v5
I'm going to go over globally styling the Gatsby Default Starter with styled-components v5, I've done this in the past with styled-components v4 but I've changed my approach and want to document it.
I'll be swapping out the styles included with a CSS reset and adding
in global style with the styled-components createGlobalStyle
helper
function and also adding in the styled-components theme provider.
To start I'll make a new Gatsby project using npx:
1npx gatsby new gatsby-starter-styled-components
Install styled-components dependencies
I'm using yarn to install my dependencies, the backslash is to have the packages on multiple lines instead of one long line:
1yarn add gatsby-plugin-styled-components \2 styled-components \3 babel-plugin-styled-components \4 styled-reset
Configure gatsby-plugin-styled-components
and createGlobalStyle
Pop gatsby-plugin-styled-components
into the gatsby-config.js
file
plugins
array:
1plugins: [2 `gatsby-plugin-styled-components`,3 `gatsby-plugin-react-helmet`,4 {
Now I'm going to create a global-style.js
file in a new directory
src/theme
then import the styled-components helper function
createGlobalStyle
into that, this is where the styles for the site
are going to live now.
Create the dir and file with the terminal command:
1mkdir src/theme && touch src/theme/global-style.js
The base styles go in here, along with the styled-reset
.
To start with I'll create the GlobalStyle
object and add in the the
reset.
1import { createGlobalStyle } from 'styled-components'2import reset from 'styled-reset'34export const GlobalStyle = createGlobalStyle`5 ${reset}6`
Remove current styling
Remove the current styling that is used in the <Layout>
component,
it's the import './layout.css'
line, I'll also delete the
layout.css
file as I'm going to be adding in my styles.
1import { graphql, useStaticQuery } from 'gatsby'2import PropTypes from 'prop-types'3import React from 'react'4import Header from './header'5import './layout.css'
Now the site has the base browser default styles, time to add in my own styles. Before that I'm going to confirm the reset is doing it thing.
Confirm CSS reset
Now I have the base browser styles I'm going to confirm the CSS reset
in the <Layout>
component. This is where I've removed the previous
global styles (layout.css
) from.
1import { graphql, useStaticQuery } from "gatsby"2import PropTypes from "prop-types"3import React from "react"4import { GlobalStyle } from "../theme/global-style"5import Header from "./header"67const Layout = ({ children }) => {8 // static query for the data here9 return (10 <>11 <Header siteTitle={data.site.siteMetadata.title} />12 <div13 style={{14 margin: `0 auto`,15 maxWidth: 960,16 padding: `0 1.0875rem 1.45rem`,17 }}18 >19 <GlobalStyle />20 <main>{children}</main>21 <footer>
In the code example here 👆I've I removed the useStaticQuery
hook
for readability.
Ok, cool, looks pretty reset to me!
Create the new browser base styles
Time to add in some more styles to the global style. First, the
box-sizing
reset, take a look at the CSS Tricks post on Box Sizing
for a great explanation of why we do this.
1import { createGlobalStyle } from 'styled-components'2import reset from 'styled-reset'34export const GlobalStyle = createGlobalStyle`5 ${reset}67 *, *:before, *:after {8 box-sizing: border-box;9 }10 html {11 box-sizing: border-box;12 }13`
Then I'm adding in the smooth scroll html property and some additional styles for base font size and colour along with base line height letter spacing and background colour.
1import { createGlobalStyle } from 'styled-components'2import reset from 'styled-reset'34export const GlobalStyle = createGlobalStyle`5 ${reset}67 *, *:before, *:after {8 box-sizing: border-box;9 }10 html {11 box-sizing: border-box;12 scroll-behavior: smooth;13 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;14 font-size: 16px;15 color: '#1a202c';16 }17 body {18 line-height: 1.5;19 letter-spacing: 0;20 background-color: '#f7fafc';21 }22`
Place GlobalStyle
at the top of the React tree 🌳
I'm adding this as high up the component tree as possible so that the global styles will affect everything that is 'underneath' it.
In the case of the Gatsby Default Starter you'll notice that the
<Layout>
component wraps a the index.js
page, page-2.js
and the
404.js
page so adding the <GlobalStyle />
component here is a
sound option.
There is an alternative to adding it to the layout and that is to use the Gatsby Browser and Gatsby SSR API wrapRootElement.
If I add in the following code to gatsby-browser.js
the styles are
applied.
1import React from 'react'2import Layout from './src/components/layout'3import { GlobalStyle } from './src/theme/global-style'45export const wrapRootElement = ({ element }) => (6 <>7 <GlobalStyle />8 <Layout>{element}</Layout>9 </>10)
I also have a double header, that's because the layout component is still wrapping the index page, page 2 and the 404 page. I'll remove the layout component from those locations so I have it in one place to manage.
Make a Root Wrapper to keep things DRY 🌵
I also need to add the same code into gatsby-ssr.js
so so that the
styles are rendered on the server when the site is built.
Rather than have the code duplicated in the two files I'll create a
root-wrapper.js
file (you can call it what you like!) and add it
to the root of the project. I'll import that into both the
gatsby-browser.js
and gatsby-ssr.js
files:
1import { wrapRootElement as wrap } from './root-wrapper'23export const wrapRootElement = wrap
Global fonts with gatsby-plugin-google-fonts
Onto the main reason for this post, with the v5 release of
styled-components the use of @imports
in createGlobalStyle
isn't
working, (that approach is detailed here) it's recommended that you
embed these into your HTML index file, etc.
NOTE: At this time we recommend not using @import inside of
createGlobalStyle
. We're working on better behavior for this functionality but it just doesn't really work at the moment and it's better if you just embed these imports in your HTML index file, etc.
But! As I'm using Gatsby, of course, "There's a Plugin For
That™️" so I'm going to use gatsby-plugin-google-fonts
for this,
I'm using this in place of gatsby-plugin-web-font-loader
because it
uses display=swap
.
1yarn add gatsby-plugin-google-fonts
Configure, I'll add three fonts, sans, sans serif and monospace to the
Gatsby plugin array in gatsby-config.js
:
1{2 resolve: `gatsby-plugin-google-fonts`,3 options: {4 fonts: [5 `cambay\:400,700`,6 `arvo\:400,700`,7 `ubuntu mono\:400,700`,8 ],9 display: 'swap',10 },11},
I can now use these fonts throughout my site.
styled-components Theme provider
The styled-components ThemeProvider is a great solution for managing your styles throughout a project.
Part of the inspiration for my approach came from Sid's talk at React Advanced which I wrote about and part from watching the Tailwind CSS courses from Adam Wathan on Egghead.io check out the playlist here: Introduction to Tailwind and the Utility first workflow
With the ThemeProvider I can have things like colours, sizes, font weights in one place so that there is a consistent set of presets to choose from when styling.
In the global-style.js
file I'm creating a theme object to hold all
the values for the theme.
For the font I'll add in the types I defined in the Gatsby config, for serif, sans serif and monospace.
1import { createGlobalStyle } from 'styled-components'2import reset from 'styled-reset'34export const theme = {5 font: {6 sans: 'Cambay, sans-serif',7 serif: 'Arvo, sans',8 monospace: '"Ubuntu Mono", monospace',9 },10}1112export const GlobalStyle = createGlobalStyle`13 ${reset}1415 *, *:before, *:after {16 box-sizing: border-box;17 }18 html {19 box-sizing: border-box;20 scroll-behavior: smooth;21 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;22 font-size: 16px;23 color: '#1a202c';24 }25 body {26 line-height: 1.5;27 letter-spacing: 0;28 background-color: '#f7fafc';29 }30`
Now I'll need to add the <ThemeProvider>
high up in the React render
tree, same as with the global style, I'll add it to the
root-wrapper.js
file.
1import React from 'react'2import { ThemeProvider } from 'styled-components'3import Layout from './src/components/layout'4import { GlobalStyle, theme } from './src/theme/global-style'56export const wrapRootElement = ({ element }) => (7 <ThemeProvider theme={theme}>8 <GlobalStyle />9 <Layout>{element}</Layout>10 </ThemeProvider>11)
When I want to pick a font type to use in the project I can use the
theme
object and pick out the desired type.
Like setting the HTML font-family to sans serif:
1export const GlobalStyle = createGlobalStyle`2 ${reset}34 *, *:before, *:after {5 box-sizing: border-box;6 }7 html {8 box-sizing: border-box;9 scroll-behavior: smooth;10 font-family: ${({ theme }) => theme.font.sans};11 font-size: 16px;12 color: '#1a202c';13 }14 body {15 line-height: 1.5;16 letter-spacing: 0;17 background-color: '#f7fafc';18 }19`
The base font is now set to Cambay, why stop there though, I'll bring
in some fonts sizes and font weights from the Tailwind full config
and add them to the theme
object.
1import { createGlobalStyle } from 'styled-components'2import reset from 'styled-reset'34export const theme = {5 font: {6 sans: 'Cambay, sans-serif',7 serif: 'Arvo, sans',8 monospace: '"Ubuntu Mono", monospace',9 },10 fontSize: {11 xs: '0.75rem',12 sm: '0.875rem',13 base: '1rem',14 lg: '1.125rem',15 xl: '1.25rem',16 '2xl': '1.5rem',17 '3xl': '1.875rem',18 '4xl': '2.25rem',19 '5xl': '3rem',20 '6xl': '4rem',21 },22 fontWeight: {23 hairline: '100',24 thin: '200',25 light: '300',26 normal: '400',27 medium: '500',28 semibold: '600',29 bold: '700',30 extrabold: '800',31 black: '900',32 },33}3435export const GlobalStyle = createGlobalStyle`36 ${reset}3738 *, *:before, *:after {39 box-sizing: border-box;40 }41 html {42 box-sizing: border-box;43 scroll-behavior: smooth;44 font-family: ${({ theme }) => theme.font.sans};45 font-size: ${({ theme }) => theme.fontSize.lg};46 color: '#1a202c';47 }48 body {49 line-height: 1.5;50 letter-spacing: 0;51 background-color: '#f7fafc';52 }53`
I'll add the base font at .lg
(1.125rem
), I'll also add in line
height and line spacing defaults but I'll save adding the whole config
here to save you a codewall, you get the idea though, right?
Here's the rest of the GlobalStyle with defaults applied.
1export const GlobalStyle = createGlobalStyle`2 ${reset}34 *, *:before, *:after {5 box-sizing: border-box;6 }7 html {8 box-sizing: border-box;9 scroll-behavior: smooth;10 font-family: ${({ theme }) => theme.font.sans};11 font-size: ${({ theme }) => theme.fontSize.lg};12 color: ${({ theme }) => theme.colours.grey[900]};13 }14 body {15 line-height: ${({ theme }) => theme.lineHeight.relaxed};16 letter-spacing: ${({ theme }) => theme.letterSpacing.wide};17 background-color: ${({ theme }) => theme.colours.white};18 }19`
Shared Page Elements
The current page is still missing basic styles for h1
and p
so I'm
going to create those in a new directory
src/components/page-elements
1mkdir src/components/page-elements2touch src/components/page-elements/h1.js3touch src/components/page-elements/p.js
And add some base styles to those for h1
:
1import styled from 'styled-components'23export const H1 = styled.h1`4 font-size: ${({ theme }) => theme.fontSize['4xl']};5 font-family: ${({ theme }) => theme.font.serif};6 margin-top: ${({ theme }) => theme.spacing[8]};7 line-height: ${({ theme }) => theme.lineHeight.none};8`
And the same sort of thing for the p
:
1import styled from 'styled-components'23export const P = styled.p`4 font-size: ${({ theme }) => theme.fontSize.base};5 margin-top: ${({ theme }) => theme.spacing[3]};6 strong {7 font-weight: bold;8 }9 em {10 font-style: italic;11 }12`
Then it's a case of replacing the h1
's and p
's in the project to
use the styled components.
Here's the index.js
file as an example:
1import { Link } from 'gatsby'2import React from 'react'3import Image from '../components/image'4import { H1 } from '../components/page-elements/h1'5import { P } from '../components/page-elements/p'6import SEO from '../components/seo'78const IndexPage = () => (9 <>10 <SEO title="Home" />11 <H1>Hi people</H1>12 <P>Welcome to your new Gatsby site.</P>13 <P>Now go build something great.</P>14 <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>15 <Image />16 </div>17 <Link to="/page-2/">Go to page 2</Link>18 </>19)2021export default IndexPage
Export all your styled-components from an index file
As the amount of page elements grows you may want to consider using an
index.js
file instead of having an import for each individual
component you can import from one file.
Let's take a quick look at that, so let's say I import the h1
and
p
into a file, it'll looks something like this:
1import { H1 } from '../components/page-elements/h1'2import { P } from '../components/page-elements/p'
If you have several elements your using in the file the imports could get a bit cluttered.
I've taken to creating an index.js
file that will export all the
components, like this:
1export * from './h1'2export * from './p'
Then when importing the components it will look like this:
1import { H1, P } from '../components/page-elements'
That's it for this one!
Here's a video detailing the process 📺
Thanks for reading 🙏
Please take a look at my other content if you enjoyed this.
Follow me on Twitter or Ask Me Anything on GitHub.