Skip to content

Gatsby starter with modular file structure and support for Styled Components.

Notifications You must be signed in to change notification settings

jovanovskajovana/gatsby-starter-styled-components

Repository files navigation

Gatsby starter with Styled Components

Here is a ready-to-use Gatsby starter with Styled Components, ESLint, SEO optimization and more.

🚀 Setup

With Gatsby, we can write React-based components that will be turned into static HTML, CSS, and JS at build time. Splitting the code by components makes it faster for users to load only what they need, and with route-based prefetching, to get data based on the page they are currently on. All page content is server-side rendered, leading to extremely fast loading times and further optimization for search engine crawlers.

Basic setup

npm install -g gatsby-cli

Make sure you have a recent version of Node.js and npm set on your machine, then install the Gatsby CLI.

gatsby new your-project-name https://github.com/jovanovskajovana/gatsby-starter-styled-components.git

Create new poject from this starter. Remember to name it as you wish, navigate to the directory, run npm run dev and start the development mode available at http://localhost:8000.


🕵️‍♀️ Dive deeper

Styled Components

Think of all the reusable modules on the site and create a styled component for each. The rule of thumb is that any logical part, such as header, button, input, or pagination, turns into its own layout and nests all of the children selectors.

import styled from 'styled-components'

const HeaderLayout = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  padding: 3rem 10rem;

  a {
    font-size: 1.2rem;
    font-weight: 400;
    color: ${(props) => props.theme.textPrimary};
  }
`

export default HeaderLayout

First you need to define styled properties for the element. It can be div, p, any other element, or extension of an already created styled component.

import React from 'react'
import { Link } from 'gatsby'

import HeaderLayout from '../styles/HeaderLayout'

const Header = () => (
  <HeaderLayout>
    <Link to="/contact">let's connect</Link>
    <Link to="/">go back home</Link>
  </HeaderLayout>
)

export default Header

Then simply import it to wrap any sub-elements or content that needs to be stylized.


You can also create your own global style normalizer with createGlobalStyle. Place the GlobalStyles to the top of the main Layout component to ensure that it is shared over all pages.

import { createGlobalStyle } from 'styled-components'

const GlobalStyles = createGlobalStyle`
  * {
    box-sizing: border-box;
    word-wrap: break-word;
  }

  html,
  body {
    font-size: 16px;
    line-height: 1.5;
    font-family: "Ubuntu", sans-serif;
  }

  ...
`

export default GlobalStyles

Creating a Layout

Most likely, you will like to have a general site structure with header, footer, centered container, some global styles, and so on. Create a generic Page component with a logically structured tree that will wrap all the children elements passed from different pages.

import React from 'react'

import GlobalStyles from '../styles/GlobalStyles'
import { PageLayout } from '../styles/PageLayout'
import { Typography } from '../styles/Typography'

import Header from './Header'

const Page = ({ children }) => {
  return (
    <>
      <GlobalStyles />
      <Typography />
      <Header />
      <PageLayout>{children}</PageLayout>
    </>
  )
}

export default Page

Gatsby Browser and Gatsby SSR API files

As your site grows bigger, you don't want to go trough every single page and wrap the Layout component around it. Instead, you can use the Gatsby wrapPageElement plugin, which will be defined in the gatsby-ssr.js and gatsby-browser.js files at the root of your project, to automatically wrap the main layout.

import React from 'react'

import Page from './src/components/Page'

export function wrapPageElement({ element, props }) {
  return <Page {...props}>{element}</Page>
}

Working with Typography

Depending on the source of the fonts, you can choose a different approach to include them in the site. When working with custom fonts, you can host them locally in the assets/fonts/ directory, and Gatsby will make sure they are moved to the static/ folder, included in the page header, and served before the site is rendered.

import styled, { createGlobalStyle } from 'styled-components'

import fontRegular from '../assets/fonts/Ubuntu-Regular.ttf'
import fontItalic from '../assets/fonts/Ubuntu-Italic.ttf'

const Typography = createGlobalStyle`
  @font-face {
    font-family: 'Ubuntu';
    src: url(${fontRegular});
    font-style: normal;
    font-weight: 400;
  }

  @font-face {
    font-family: 'Ubuntu';
    src: url(${fontItalic});
    font-style: italic;
    font-weight: 400;
  }

  ...
`
const Paragraph = styled.p`
  font-size: 0.8em;
  font-weight: 400;
`

export { Typography, Paragraph }

By importing font files into the Typography, it creates URL with a unique identifier that you can use to declare the @font-face. Just remember to set the right font-family in your GlobalStyles and also put the Typography component on top of the main Layout.

Theming

Notice how we added a ThemeProvider around the main page layout. That will give an access to the theme variables to all the styled-components in the render tree. In addition, it makes it pretty easy for you to create a dark / light theme mode or allow the user to select any of the color palette options you will offer in the theme object.

import React from 'react'
import { ThemeProvider } from 'styled-components'

import GlobalStyles from '../styles/GlobalStyles'
import { PageLayout } from '../styles/PageLayout'
import { Typography } from '../styles/Typography'
import theme from '../constants/theme'
import { useTheme } from '../hooks/useTheme'

import Header from './Header'

const Page = ({ children }) => {
  const [mode, toggleMode] = useTheme()

  const themeMode = mode === 'light' ? theme.light : theme.dark

  return (
    <>
      <GlobalStyles />
      <Typography />
      <ThemeProvider theme={themeMode}>
        <Header mode={mode} toggleMode={toggleMode} />
        <PageLayout>{children}</PageLayout>
      </ThemeProvider>
    </>
  )
}

export default Page

With the useTheme() custom hook you can capture user's preferred mode and save it to local storage.

Using Link

All the basic styling is ready and you need to create a few pages. Pages can be dynamically generated or they can be created in a file system routing. Go to the pages/ directory and, in addition to index.js as the starting point of the site, create the other pages you need.

Now Gatsby will handle the routing by creating a path for each page. Instead of using an anchor link that would reload the entire page, connect them with Gatsby Link and get the preloaded page content blazingly fast on each click.

import React from 'react'
import { Link } from 'gatsby'

import { Container } from '../styles/PageLayout'
import { Headline } from '../styles/Typography'

const NotFoundPage = () => (
  <Container column>
    <Headline>Oops, where did this page come from?</Headline>
    <Link to="/">← go back home</Link>
  </Container>
)

export default NotFoundPage

If the user visits a page that doesn't exist, Gatsby will render 404 by default, but you can create a custom page for that.

Link is intended for Gatsby-only pages, for all external connections, you can still use the standard <a> element.

Using Image

Images usually slow down your site and Gatsby is trying to fix this problem. In order to resize and compress, fix the aspect ratio, set in multiple formats and lazy load them, Gatsby must process the images.

Use the gatsby-image component, which will source your images from a directory with gatsby-source-filesystem and serve optimized images with gatsby-plugin-sharp. Make sure to configure the gatsby-config.js file with the correct path to the image folder.

import React from 'react'
import { useStaticQuery, graphql } from 'gatsby'
import Img from 'gatsby-image'

import { ImageLayout } from '../styles/ImageLayout'

export default () => (
  const data = useStaticQuery(graphql`
    query {
      file(relativePath: { eq: "image-name.png" }) {
        childImageSharp {
          fluid {
            ...GatsbyImageSharpFluid
          }
        }
      }
    }
  `)

  return (
    <ImageLayout>
      <Img fluid={data.file.childImageSharp.fluid} alt="Gatsby image" />
    </ImageLayout>
  )
}

Use the Image component to query all the images at build time and filter by the given name.

  <Image name="logo-gatsby-styled-components.png" alt="Gatsby image" minWidth="500px"></Image>

This way, you won’t have to write the same query multiple times, but processing a large number of images can take a long time. If that happens, consider querying from an external image processing service (eg. Sanity image pipeline).

For small images and icons a static import with Webpack is still an option. Similar to font files, make sure that the images are imported into the component so that Webpack will correctly move them into the public folder and provide the correct paths.

import React from "react"

import imgUrl from "../assets/images/image-name.jpg"
import { ImageLayout } from '../styles/ImageLayout'

export default () => (
  <ImageLayout>
    <img src={imgUrl} alt="Static image" />
  </ImageLayout>
)

SEO Optimization

We put title and description metadata in the header of the document to help search engines understand the content of our site and know when it should appear in search results.

import React from 'react'
import { Helmet } from 'react-helmet'
import { useStaticQuery, graphql } from 'gatsby'

const SEO = ({ children, title, description, image, location }) => {
  const { site } = useStaticQuery(graphql`
    query {
      site {
        siteMetadata {
          title
          description
          author
        }
      }
    }
  `)

  return (
    <Helmet titleTemplate={`%s - ${site.siteMetadata.title}`}>
      <html lang="en" />
      <meta charSet="utf-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
      <meta name="description" content={site.siteMetadata.description} />
      <title>{title}</title>

      ...

      {children}
    </Helmet>
  )
}

export default SEO

Create an SEO component with react-helmet that will make sure to add all metadata attributes to the static HTML pages that Gatsby builds. Then use it on all your pages to provide the page title and any other information.

Deploy

The deploy can be easily done on Netlify. In your account, simply create a new site from the Git repository. Select the main branch for production and develop for a staging environment. Netlify will recognize the gatsby build command and publish the public directory. Voilà! a Continuous Deployment (CD) is set.