DEV Community

Cover image for how to theme your gatsby site
Jose Miguel
Jose Miguel

Posted on • Updated on

how to theme your gatsby site

First of all.

The following idea can be applied to all kinds of sites while those use React, you need to know that Gatsby is a React-based open-source framework for creating websites and apps.

The main Idea 💡

We need to know that themes are CSS properties that change when we select a specific theme. eg.

          <button type="button" onClick={handleToggle} style={styles(theme).toggle}>
            { theme === themes.light ? '' : '' }
          </button>

Example of toggle a theme in a site

We will create a dark ☽ and light ☼ theme:

import { createContext, useState } from 'react'

export const themes = {
  light: {
    foreground: 'rebeccapurple',
    background: 'white'
  },
  dark: {
    foreground: 'white',
    background: 'rebeccapurple'
  }
};
export const ThemeContext = createContext();

We need to create also the hook to manage the state of the current theme:

export const useThemes = () => {
  const [mode, setMode] = useState(themes.light);

  const toggleMode = () => { 
    if (mode === themes.light) {
      setMode(themes.dark);
    } else {
      setMode(themes.light);
    }
  };

  return [mode, toggleMode]
};

Please create a context to wrap your app :

import React from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"
import { ThemeContext, themes, useThemes } from './Context/Theme'
import Header from "./Header"
import "./layout.css"

const Layout = ({ children }) => {

Import and create only one instance of our theme, this is a kind of singleton pattern.

  const [theme, handleToggle ] = useThemes();
  const data = useStaticQuery(graphql`
    query SiteTitleQuery {
      site {
        siteMetadata {
          title
        }
      }
    }
  `)
console.log(theme.foreground);
  return (
    <ThemeContext.Provider value={themes}>
      <Header siteTitle={data.site.siteMetadata.title} handleToggle={handleToggle} theme={theme}/>
      <div
        style={{
          color: theme.foreground,
          background: theme.background,
          margin: `0 auto`,
          maxWidth: 960,
          padding: `10rem 1.0875rem 1.45rem`,
        }}
      >
        <main>{children}</main>
        <footer>
          © {new Date().getFullYear()}, Built with
          {` `}
          <a href="https://www.gatsbyjs.org">Gatsby</a>
        </footer>
      </div>
    </ThemeContext.Provider>
  )
}

Layout.propTypes = {
  children: PropTypes.node.isRequired,
}

export default Layout

The Header component will look like:

import { Link } from "gatsby"
import PropTypes from "prop-types"
import React, { useContext } from "react"
import {styles} from './styles'
import { ThemeContext } from "../Context/Theme";

I will use Context to compare what is the current theme and handleToggle function to change between themes.

function Header({ siteTitle, handleToggle, theme }) {
  const themes = useContext(ThemeContext);
  return (
    <header
      style={styles(theme).header}
    >
      <div
        style={styles(theme).navbar}
      >
        <h1 style={styles(theme).title}>
          <Link
            to="/"
            style={styles(theme).link}
          >
            {siteTitle}
          </Link>
        </h1>
        <div style={styles(theme).buttonContainer}>
          <button type="button" onClick={handleToggle} style={styles(theme).toggle}>
            { theme === themes.light ? '' : '' }
          </button>
        </div>
      </div>
    </header>
  );
}

Header.propTypes = {
  siteTitle: PropTypes.string,
}

Header.defaultProps = {
  siteTitle: ``,
}

export default Header

Wrapping up

The main idea is to know that you can apply this to any site ;)

Top comments (4)

Collapse
 
kira profile image
Kira

Hi, I used the same way to build my theme, however, the children component inside couldn't get ThemeContext... Do you know how to solve it?

Collapse
 
josepplloo profile image
Jose Miguel

Also look that I only themed the Header component, Main and Footer components need the prop theme or reach themes via useContext

Collapse
 
josepplloo profile image
Jose Miguel

Hi! Could you please show me a snippet of code?
Maybe you have to assign some css classes to the children

Collapse
 
kira profile image
Kira

Hi, I found the solution. Before, I wrapped ThemeContext inside layout

export const Layout = (props)=>{
    <ThemeContext.Provider value={themes}>
    <Header/>
        {props.children}
    <Footer/>
    </ThemeContext.Provider>
}
Enter fullscreen mode Exit fullscreen mode

Then in my page Blog, I coudln't get themeContext

export const Page = ()=>{
    const themes = useContext(ThemeContext) //can't get the value
    ...

    return (
        <Layout>
            page
        </Layout>
    )
}
Enter fullscreen mode Exit fullscreen mode

The solution is to create a wrapRootElement in gatsby-browser.js. Then all pages could get ThemeContext's value.

const wrapRootElement =({element})=>{
    return(
    <ThemeContext.Provider value={themes}>
        {element}    
    </ThemeContext.Provider>
    )
}
Enter fullscreen mode Exit fullscreen mode