In Chapter 1, we covered some of the most important foundations you’ll need to be familiar with as you begin working with Gatsby, and walked through creating a rudimentary “Hello World” site in Gatsby in your local development environment. In this chapter, we’ll dive into what we covered at the end of Chapter 1 in more detail. I’ll provide more information about the Gatsby command-line interface, working with starters, how Gatsby pages and components work, the layout component, CSS in Gatsby, and how to add plugins to your site.
We explored the Gatsby CLI at some length at the end of Chapter 1, because it is the primary conduit by which you, as a Gatsby developer, will interact with your Gatsby sites. Many common tasks for Gatsby development can be performed with the Gatsby CLI, and we’ll return frequently to Gatsby CLI commands throughout this book. For this reason, it’s only logical that we start with a closer look at CLI and what it can do to accelerate your Gatsby development workflows.
As you saw in Chapter 1, the Gatsby CLI is the primary means by which developers interact with Gatsby sites during development. It’s the main way to set up a new Gatsby application through scaffolding (generation of files and directories adhering to a boilerplate template) and to run a development server for debugging. In this overview of the Gatsby CLI, we’ll take another look and discuss some of the most important commands.
The Gatsby CLI is an executable JavaScript package named gatsby-cli
that you can leverage from anywhere in your filesystem if you install it globally, though some commands will only work in directories where there is a Gatsby codebase present. As we covered briefly in the previous chapter, the Gatsby CLI is available in the NPM registry, and it’s recommended that all users install (i
is shorthand for install
) it globally by running:
$
npm
i
-g
gatsby-cli
To see a list of commands and a help mode to help you navigate the internals of the Gatsby CLI, you can run the following help command, which begins with the word gatsby
, as all Gatsby CLI commands do:
$
gatsby
--help
Some starters wrap their Gatsby CLI commands in NPM scripts, which are executable commands that can be defined within a package.json file. In NPM projects, the package.json file contains information about the project in question and all of its dependencies, along with other important information such as commands specific to the project.
Now that we’ve covered the Gatsby CLI installation process and help mode, we’ll turn to some of the most frequently invoked commands in the Gatsby CLI and why they are relevant to your own work.
In certain scenarios, you may wish to use NPM scripts to execute Gatsby CLI commands. To allow for developers to create aliases to use NPM scripts to execute Gatsby CLI commands on your behalf, add the following to your package.json file. Check to make sure that another NPM script isn’t already present; if one is, add this alongside the existing script:
{
"scripts"
:
{
"develop"
:
"gatsby develop"
}
}
Now, instead of running gatsby develop
to access the gatsby develop
command, you can also run the following command according to the defined NPM script:
$ npm run develop
The gatsby new
command is responsible for the creation of new Gatsby sites based on specific parameters. It takes the following format:
$ gatsby new [site-name [starter-url]]
The gatsby new
command accepts two arguments:
site-name
starter-url
starter-url
argument, the Gatsby CLI will supply gatsby-starter-default
by default.The site-name
argument must only contain letters and numbers, and the gatsby new
command will throw an error if you specify .
, ./
, or a space in your site name argument.
To create a new Gatsby site named my-great-gatsby-site
with the default starter, you can execute the gatsby new
command without providing a starter-url
argument:
$
gatsby
new
my-great-gatsby-site
To create a new Gatsby site with a specific starter, you can supply the URL or local file path to the starter as the starter-url
argument. In this example, we’re using gatsby-starter-blog
as the starter of choice:
$
gatsby
new
my-great-gatsby-site
https://github.com/gatsbyjs/gatsby-starter-blog
You can also refer to a starter by using just a GitHub username and repository. Here is an example of the same command using this approach:
$
gatsby
new
my-great-gatsby-site
gatsbyjs/gatsby-starter-blog
To supply your own local custom starter located on your machine’s filesystem, use:
$ gatsby new my-great-gatsby-site ~/path/to/my/custom/starter
If you decide not to supply either of the two arguments, the Gatsby CLI will prompt you to supply them through an interactive shell interface that looks like the following:
$
gatsby
new
create-gatsby
version
1.3.0
Welcome
to
Gatsby!
This
command
will
generate
a
new
Gatsby
site
for
you
in
/Users/prestonso/projects
with
the
setup
you
select
.
Let
'
s
answer
some
questions:
What
would
you
like
to
call
your
site?
?
>
My
Gatsby
Site
The gatsby new
command is the quickest entry point to start developing a Gatsby site out of the box with minimal overhead. Though you can certainly write your own Gatsby site from scratch, you will find it much easier to use one of the existing default starters and adapt it to your needs rather than kicking off a new codebase from scratch.
Now that we’ve covered how the Gatsby CLI supports site creation, we’ll turn to more details about gatsby develop
, the command you executed to see your site in a browser at the end of Chapter 1.
The gatsby develop
command can be executed within the root directory of any Gatsby project:
$
gatsby
develop
Once you’ve created your new Gatsby site, you can change directories (cd
) into the root of the project and execute gatsby develop
immediately:
$
gatsby
new
my-great-gatsby-site
$
cd
my-great-gatsby-site
$
gatsby
develop
The gatsby develop
command initializes a local development server that includes hot reloading (automatic browser refreshing) when your code is updated. This means you can keep gatsby develop
running indefinitely while you modify your code and preview the changes in your browser. To exit and shut down the local development server, simply press Ctrl-C.
The gatsby develop
command accepts several options:
--host
or -H
gatsby develop
command and defaults to localhost
for local development.--port
or -p
env.PORT
(if you have environment variables defined) or 8000
(if none is set).--open
or -o
--https
or -S
The gatsby develop
command can also be used with the host option (--host
or -H
) to access your development environment from other devices that are on the same network. To do this, execute the following command:
$
gatsby
develop
-H
0.0.0.0
The terminal output will then log information as normal, and it will also provide you with a URL that you can share with those using other devices:
You can now view gatsbyjs.com in the browser. ⠀ Local: http://0.0.0.0:8000/ On Your Network: http://192.168.0.212:8000/
Navigate to that URL within a device on the same network to see how the site renders outside your local machine.
When previewing code changes on your local machine instead of for other devices, with this host set, you have the option to use http://localhost:8000 or the URL listed alongside “On Your Network” to see your Gatsby site in action.
The gatsby build
command is used to compile a complete Gatsby site and ready it for deployment to production (such as to a content delivery network, or CDN). Generally speaking, it’s the last step you’ll take before making your site available for access on the web:
$
gatsby
build
The gatsby build
command must be executed in the root directory of your project and accepts several options. Note here that one of the options, --prefix-paths
, relies on information from gatsby-config.js, which we’ll discuss after this exploration of the Gatsby CLI:
--prefix-paths
pathPrefix
in your Gatsby configuration).--no-uglify
–profile
--open-tracing-config-file
--graphql-tracing
--no-color
or --no-colors
In addition to these options to cater your Gatsby build process to your needs, there are optional build environment variables that allow you to influence the outcome of your command. If you’re familiar with environment variables (if not, we’ll cover them in Chapter 12), you can set CI=true
as an environment variable to cater to dumb terminals that aren’t as robust as modern terminals. You can also set NO_COLOR
as an environment variable to disable color-coded terminal output.
The gatsby serve
command is used to serve the production build of your site from a local development server so you can see the production version of your site in a browser:
$
gatsby
serve
The gatsby serve
command must be executed in the root directory of your project and accepts several options:
--host
or -H
localhost
.--port
or -p
9000
.--open
or -o
--prefix-paths
pathPrefix
you configure in your Gatsby configuration.These are the major commands you will use on a regular basis when interacting with your Gatsby site.
There are several other Gatsby commands that are useful for Gatsby development but might not be used as frequently as the others. These commands often are responsible for displaying information about a site, clearing the cache folder (.cache) for a site, and directing you to documentation about plugins.
The gatsby info
command is used to deliver helpful information about the environment in which a Gatsby site is displayed so that when a bug is reported, Gatsby’s maintainers can reproduce the situation that led to the issue:
$
gatsby
info
The gatsby info
command must be executed in the root directory of your project and accepts one option: --clipboard
or -C
, which automatically copies the environment information displayed in terminal output to your clipboard so you can send it to others.
The gatsby clean
command is used to wipe out any items in Gatsby’s .cache folder, its internal cache, and public directories where compiled files are delivered:
$
gatsby
clean
The gatsby clean
command must be executed in the root directory of your project and is most useful as a last resort when your codebase seems to be having issues like the following:
gatsby clean
can ensure that GraphQL accounts for all new resources.The gatsby clean
command can be useful in a variety of situations that may surface during Gatsby development, and we’ll return to examine some of these in later chapters.
The gatsby plugin
command is used to execute commands that are relevant to Gatsby plugins, add-ons from the Gatsby ecosystem. Executing gatsby plugin docs
will transport you to documentation about how to use, install, and write plugins:
$
gatsby
plugin
docs
The gatsby repl
command is used to open a Node.js read–eval–print loop (REPL) within the context of your Gatsby environment:
$
gatsby
repl
As you interact with gatsby repl
, Gatsby will prompt you to type in certain commands and explore by displaying:
$
gatsby
>
You can then type in a REPL command to understand more about how your site data is used within Gatsby.
As we saw briefly in Chapter 1, starters are boilerplate Gatsby site templates managed by the Gatsby community that are intended to get you started quickly with Gatsby development. When you execute a gatsby new
command with a starter URL, it clones that boilerplate starter’s repository, installs any required dependencies, and clears its Git commit history so you can start with a clean slate.
The Gatsby ecosystem offers a range of official starters, which are maintained by the Gatsby project team. Many new Gatsby developers begin with the gatsby-starter-hello-world
starter, as we did in Chapter 1, before moving on to the gatsby-starter-default
starter.
Table 2-1 lists the official starters and their most salient features. We’ll cover the starters for Gatsby themes in later chapters.
Starter | GitHub URL | Use case | Features |
---|---|---|---|
gatsby-starter-default |
https://github.com/gatsbyjs/gatsby-starter-default | Adequate for most use cases | Baseline Gatsby site |
gatsby-starter-blog |
https://github.com/gatsbyjs/gatsby-starter-blog | Create a rudimentary blog | Blog post pages and blog post listings |
gatsby-starter-hello-world |
https://github.com/gatsbyjs/gatsby-starter-hello-world | Get started with Gatsby | Bare essentials of a Gatsby site |
gatsby-starter-blog-theme |
https://github.com/gatsbyjs/gatsby-starter-blog-theme | Create a blog with blog posts and pages | Gatsby themes |
gatsby-starter-theme-workspace |
https://github.com/gatsbyjs/gatsby-starter-theme-workspace | Get started with Gatsby themes | Gatsby themes |
The gatsby-starter-default
starter is the most common starting point for Gatsby developers beginning a site implementation from scratch. During our upcoming inspection of Gatsby’s page strategy, I’ll talk more about key concepts such as pages, templates, and components.
Gatsby also allows for community contributors to submit starters for inclusion in the community starter library, a compendium of starters built by members of the Gatsby community. We’ll return later in this book to the topic of how to create your own custom starter for yourself or for the community.
All Gatsby starters are fully functional websites off the shelf, but the vast majority of Gatsby developers will wish to modify a starter and customize it according to their needs. For this reason, it’s important to select a starter that satisfies your requirements, whether you’re building a blog, a heavy-duty content or commerce site, or something else. There are four major areas within a given starter where you will need to do work:
With the exception of data from external and internal sources, which deserves a much deeper dive, let’s look at each of these typical areas in turn. We’ll create a new project that will guide us through the remainder of Chapter 2 as we explore the gatsby-config.js file, Gatsby pages and components, Gatsby’s layout component, and using CSS with Gatsby.
In your terminal, execute the following command:
$
gatsby
new
gtdg-ch2-pages-components
gatsbyjs/gatsby-starter-hello-world
Note here that we are creating a new directory, gtdg-ch-2-pages-components, to contain our new Gatsby site, and we’re using the GitHub username and repository strategy to retrieve the Gatsby starter repository. Next, change into this directory:
$
cd
gtdg-ch2-pages-components
and open the gatsby-config.js file in the code editor of your choice. For example, to open the file in Visual Studio Code, use:
$ code gatsby-config.js
and for Atom, use:
$ atom gatsby-config.js
Gatsby has no opinion about what code editor you use in developing a Gatsby site. Many developers prefer Visual Studio Code or Atom due to their rich capabilities for language- and framework-specific extensions and debugging tools. These code editors also have the benefit of being openable with a single command.
If you followed the steps to open gatsby-config.js in the code editor of your choice, you’ll see the following contents:
/**
* Configure your Gatsby site with this file.
*
* See: https://www.gatsbyjs.com/docs/gatsby-config/
*/
module
.
exports
=
{
/* Your site config here */
plugins
:
[],
}
This empty gatsby-config.js file has no configuration options set and no plugins loaded. The very first step to make your Gatsby site your own is to add some information about the site, specifying the page title and other page metadata. Add a new siteMetadata
object to the top of your gatsby-config.js file, and add some information about your site. You can use this example to get some ideas:
module
.
exports
=
{
siteMetadata
:
{
title
:
`My New Gatsby Site`
,
description
:
`Check out Gatsby with this easy-to-use site.`
,
author
:
`@prestonso`
,
},
plugins
:
[],
}
Congratulations! You’ve now made your first Gatsby site your very own by identifying it as a unique Gatsby site with your own information. Now, let’s turn our attention to what’s happening inside the Gatsby site—and that means opening up one of the pages in our starter.
Pages and components are typical entry points for developers when building a Gatsby site. Pages are the higher-level components that Gatsby uses to build a site, while components can be nested within those pages and in each other. Any time you add a new page, Gatsby will automatically recognize it and incorporate it into your site build.
So how exactly does this work? In this section, we’ll take a close look at Gatsby pages, Gatsby components, and how to link between Gatsby pages.
We’ll begin with the most important atomic unit of Gatsby sites: the page. In Gatsby, any React component defined with a name in the form src/pages/*.js will automatically be recognized by Gatsby as a page and will be rendered as such. In our “Hello World” starter, we already have one page in src/pages, named index.js.
A typical need for any bona fide website is an about page, so let’s add an about page that contains a bit of information about the site. Because Gatsby recognizes any new file named src/pages/*.js as a new component, we can simply create a file named about.js and place it in that directory.
First, create a new file located at src/pages/about.js, and paste the following inside:
import
React
from
"react"
export
default
function
About
()
{
return
(
<
div
>
<
h1
>
About
us
<
/h1>
<
p
>
Welcome
to
our
about
page
!
Here
is
some
info
about
us
.
<
/p>
<
/div>
)
}
Then, if you aren’t already running gatsby develop
, go ahead and start your development server from the root of the project:
$ gatsby develop
Now, if you navigate to https://localhost:8000/about, you’ll see your new about page. And you didn’t have to do any work besides adding a file to the pages directory!
There’s much more to know about pages, including how Gatsby generates pages, how Gatsby builds pages from components, and how to hook into the page-building process. In addition, some pages have templates, a shared format across pages that is used to programmatically render pages of a certain category (such as individual blog articles or product pages). For now, however, this is an adequate introduction to pages in Gatsby and allows us to proceed to Gatsby components.
In Gatsby, components are just like React components in the traditional sense: they are portions of a page or repeatable code that needs to be placed in a separate file for reuse in multiple places, or simply to make the code more maintainable. In the context of a Gatsby page, a component might be a header, footer, or sidebar. It could also be a lead generation form or a contact form, or it could simply introduce additional code that is important to the site, such as an embedded Google Analytics snippet.
Let’s create a footer component that will be used across both our home page and our about page. First, create a new directory located at src/components. Create a file in that directory named footer.js and insert the following code:
import
React
from
"react"
export
default
function
Footer
()
{
return
<
p
>&
copy
;
2020
My
Gatsby
Site
.
All
rights
reserved
.
<
/p>
}
Now, let’s modify each of the pages to use our new footer component. First, let’s add the footer to our about page by adding an additional import
statement and invoking the component in our JSX:
import
React
from
"react"
import
Footer
from
"../components/footer"
export
default
function
About
()
{
return
(
<
div
>
<
h1
>
About
us
<
/h1>
<
p
>
Welcome
to
our
about
page
!
Here
is
some
info
about
us
.
<
/p>
<
Footer
/>
<
/div>
)
}
Now we’ll do the same in src/pages/index.js:
import
React
from
"react"
import
Footer
from
"../components/footer"
export
default
function
Home
()
{
return
(
<
div
>
<
p
>
Hello
world
!<
/p>
<
Footer
/>
<
/div>
)
}
React allows us to define what properties (props) a component should have by adding attributes to JSX elements. Instead of using the same copyright text on both pages, let’s have the page define what year the copyright statement should have, rather than the footer component itself. Modify src/components/footer.js to the following:
import
React
from
"react"
export
default
function
Footer
(
props
)
{
return
<
p
>&
copy
;
{
props
.
copyrightYear
}
My
Gatsby
Site
.
All
rights
reserved
.
<
/p>
}
Now, on our about page, let’s provide the prop that our footer needs to fill in the missing copyright year:
import
React
from
"react"
import
Footer
from
"../components/footer"
export
default
function
About
()
{
return
(
<
div
>
<
h1
>
About
us
<
/h1>
<
p
>
Welcome
to
our
about
page
!
Here
is
some
info
about
us
.
<
/p>
<
Footer
copyrightYear
=
"2020"
/>
<
/div>
)
}
Now when you navigate to the about page at https://localhost:8000/about, you’ll see the same copyright message, but this time you’re defining that prop within your use of the footer component rather than in the footer component itself.
There is a special type of component that exists in Gatsby known as the layout component, which is commonly used to define shared behavior across pages. We’ll discuss this in “The Layout Component”.
Now that you have a rudimentary understanding of pages and components and how they interrelate, we can turn our attention to another type of component that exists in Gatsby and is, in fact, unique to the framework: the Gatsby <Link />
component, which facilitates linking between Gatsby pages. Let’s take a look at how Gatsby performs routing between individual Gatsby pages.
First, let’s open our home page and add in a <Link />
component to our about page. Note that we need to import the <Link />
component from Gatsby and use it in our page:
import
React
from
"react"
import
{
Link
}
from
"gatsby"
import
Footer
from
"../components/footer"
export
default
function
Home
()
{
return
(
<
div
>
<
p
>
Hello
world
!<
/p>
<
p
><
Link
to
=
"/about/"
>
About
<
/Link></p>
<
Footer
copyrightYear
=
"2020"
/>
<
/div>
)
}
Now, let’s add a link back to the home page from our about page:
import
React
from
"react"
import
{
Link
}
from
"gatsby"
import
Footer
from
"../components/footer"
export
default
function
About
()
{
return
(
<
div
>
<
h1
>
About
us
<
/h1>
<
p
>
Welcome
to
our
about
page
!
Here
is
some
info
about
us
.
<
/p>
<
p
><
Link
to
=
"/"
>
Home
<
/Link></p>
<
Footer
copyrightYear
=
"2020"
/>
<
/div>
)
}
Success! Now we have linking between two distinct Gatsby pages using the Gatsby <Link />
component.
The Gatsby <Link />
component should be used for any linking between Gatsby pages. For external links or for anchor links, you should still use the HTML <a>
tag rather than <Link />. <Link />
does not work for external URLs outside the immediate Gatsby site, as external URLs are not part of Gatsby’s purview.
Before we turn our attention to CSS, we need to cover one of the most important components in Gatsby, which has a considerable impact on how we leverage CSS. Layout components are sections of your Gatsby site that should be shared across multiple Gatsby pages and that are most frequently used to provide styling across those pages (unlike the footer component we created in the previous section).
By default, Gatsby never applies a particular layout to pages, because it adheres to React’s compositional approach of importing and using nestable components. Component nestability allows for rich hierarchies within layouts on Gatsby sites, and you can create a header and footer that appear on every page, as well as certain components that should only appear on certain pages. Because all Gatsby components are React components, passing data between layout and page components is a trivial problem to solve.
Layout components are important in Gatsby because they are how we, as Gatsby developers, can indicate to Gatsby that pages with different layouts should display different components. On the main page of a Gatsby site, for instance, a global footer and header might be present, but developers may wish to add an additional sidebar with a menu on internal pages.
Gatsby strongly recommends placing your layout components alongside your other Gatsby components within the same directory, such as src/components/. Here’s an example of a rudimentary layout component adapted from the Gatsby documentation, which we would place into a file located at src/components/layout.js:
import
React
from
"react"
export
default
function
Layout
({
children
})
{
return
(
<
div
style
=
{{
margin
:
`0 auto`
,
maxWidth
:
960
,
padding
:
`0 1.5rem`
}}
>
{
children
}
<
/div>
)
}
Note here that we’re using inline styles on the surrounding <div>
element to indicate certain styles, but we’ll soon refactor this code.
You should recast CSS property names that contain hyphens from kebab-case
to camelCase
in JSX, because kebab-case
can lead to issues later on in JavaScript.
Now, we can return to our home page and apply this layout to the page’s contents such that all of the markup on the page is surrounded by our layout component. Any page or template we want to have this layout should use the <Layout>
component we’ve defined. Note here that we’re importing the layout we just created into our home page:
import
React
from
"react"
import
Layout
from
"../components/layout"
export
default
function
Home
()
{
return
(
<
Layout
>
<
h1
>
I
'
m
in
a
layout
!<
/h1>
<
/Layout>
);
}
And now that we have our first layout component, we can define our first global stylesheet.
Because Gatsby does not normally surround pages in a layout component by default, the top-level component is the page component itself. This means that when the page component updates during route changes, React will perform asynchronous rendering of all child components contained therein. If you’re using shared components in your layout, such as a navigation menu, those components will unmount and remount in React, thereby breaking CSS transitions or interrupting React state management in those shared components. To avoid this issue, you can wrap page components that shouldn’t get unmounted on page changes with the wrapPageElement
API, which is one of Gatsby’s Browser APIs, or the gatsby-plugin-layout
plugin, which implements wrapPageElement
for you.
There are several different styling approaches in Gatsby that allow you to adjust the typography, colors, and layout of your Gatsby website. In this section, we’ll cover each of the three CSS approaches that Gatsby makes available:
It’s important to note that Gatsby has no opinion on or prescription for which CSS paradigm you should use, and use of all three is widespread in the Gatsby community. In addition, all three of these options have both official and community plugins that can help you avoid any headaches around dependency management. Let’s take a closer look at each.
In traditional CSS approaches, globally scoped CSS is found in external stylesheets that have the .css extension, according to standard CSS specificity and inheritance. There are two ways to use global stylesheets in Gatsby: without a layout component and with a layout component.
Applying global styles with a layout component is the quickest way to apply an already existing CSS stylesheet to a Gatsby site or to write CSS that adheres to traditional CSS approaches and will work in every browser.
To use global CSS styles with a layout component, we can add a shared layout component that surrounds all shared components across the site, including styles, header and footer components, and other common components like sidebars and navigation menus. Gatsby’s default starter uses this same approach, but to learn how the layout component works with global styles, we’ll use the “Hello World” starter instead.
Create a new Gatsby site on your local machine. Note here that we’re using the GitHub username/repository strategy to target the correct starter:
$
gatsby
new
gtdg-ch2-global-styling-with-layout
gatsbyjs/gatsby-starter-hello-world
$
cd
gtdg-ch2-global-styling-with-layout
Next, create a new directory located at src/components and create two new files named layout.js and layout.css within it. The layout.js file represents our layout component, while the layout.css file represents the styles associated with that layout component.
Now, we can take the same styles we defined inline in the previous section and add them to src/components/layout.css:
/* src/components/layout.css */
div
{
margin
:
0
auto
;
max-width
:
960px
;
padding
:
0
1.5rem
;
}
For good measure, let’s also add a readily identifiable CSS rule that will be noticeable as soon as we load the page:
/* src/components/layout.css */
div
{
/* Previous lines of CSS */
background-color
:
blue
;
}
Now, inside src/components/layout.js, let’s import the stylesheet we just created at src/components/layout.css and export the layout component so any other component can use it as well:
import
React
from
"react"
import
"./layout.css"
export
default
function
Layout
({
children
})
{
return
<
div
>
{
children
}
<
/div>
}
Note that our <div>
element no longer needs any inline styles because we’ve applied our global styles to the layout component.
The last step is to update our home page in Gatsby with our layout component so that the home page uses the layout component as well as its associated styles:
import
React
from
"react"
import
Layout
from
"../components/layout"
export
default
function
Home
()
{
return
<
Layout
>
Hello
world
!<
/Layout>
}
If we now run gatsby develop
in the root of our project, we’ll see our page containing a <div>
that has a blue background color:
$
gatsby
develop
There are situations, however, where using the layout component may not be preferable. Next, we’ll discuss how to write global stylesheets without leveraging the layout component.
There are situations where you won’t want to use a layout component to drive global styles, whether due to the way you’ve architected components in your Gatsby site or because another element of your Gatsby site prevents the use of the layout component. Fortunately, you can hook into Gatsby’s browser API to include a global stylesheet separately from the React paradigm.
There is no way to apply styles globally without the layout component and leverage CSS-in-JS at the same time. You will need to use shared components (like the layout component) in order to share styles across components in CSS-in-JS.
Let’s spin up a new “Hello World” Gatsby site to take a look at how we can leverage gatsby-browser.js instead of a layout component to drive global styling. First, let’s create a new copy of the “Hello World” starter:
$
gatsby
new
gtdg-ch2-global-styling-without-layout
gatsbyjs/gatsby-starter-hello-world
$
cd
gtdg-ch2-global-styling-without-layout
Now, let’s create a stylesheet and save it to src/styles/global.css to reflect our new global stylesheet:
/* src/styles/global.css */
div
{
margin
:
0
auto
;
max-width
:
960px
;
padding
:
0
1.5rem
;
background-color
:
blue
;
}
Then we’ll create a new file named gatsby-browser.js in the root folder of our project, and import our global stylesheet into gatsby-browser.js:
import
"./src/styles/global.css"
You can use require
instead if you prefer, as in require('./src/styles/global.css')
. In addition, Gatsby has no opinion about where you place a global stylesheet with this approach, so the path src/styles/global.css is arbitrary.
While global stylesheets are the primary means by which developers have written CSS for many years, there are significant disadvantages to global styles, including conflicts between names and instances of unintended inheritance. That’s where component stylesheets can help.
While many Gatsby developers use global stylesheets to keep all their styles in one place, others prefer to split their CSS into component-level CSS files or even individual class declaration blocks that apply to a single Gatsby component. You can decompose your global stylesheet into multiple constituent stylesheets to allow team members to work separately without running into each other, only importing those CSS files that you need for a particular page, template, or component.
For instance, you can create individual CSS files for different components in the components directory (for better organization) and import them into your components. Consider a scenario where you need to import a “footer” component that has the same styles that we already applied previously:
/* src/components/footer.css */
div
{
margin
:
0
auto
;
max-width
:
960px
;
padding
:
0
1.5rem
;
background-color
:
blue
;
}
Now, back in our footer component, all we need to do is to import the CSS file:
import
React
from
"react"
import
"header.css"
export
default
function
Footer
({
children
})
{
return
(
<
div
>
{
children
}
<
/div>
)
}
While this form of using CSS files can lead to independent work streams thanks to the separation of concerns in stylesheets, it doesn’t take advantage of some of the most important ways Gatsby improves CSS performance, because it doesn’t use the layout component strategy, and Gatsby doesn’t manage the styles itself. Leveraging this approach to importing CSS files will remove your ability to take advantage of CSS performance enhancements like dead code elimination.
Because these stylesheets still require global namespaces, as a footer.css file using global selectors will have its styles apply across the entire site as opposed to being scoped solely to a footer component, they can present many of the same issues as global stylesheets do. In the next sections, we’ll discuss how JavaScript can help us scope our CSS locally instead of globally with CSS Modules and CSS-in-JS.
Gatsby offers two approaches to writing CSS that is scoped to components: CSS Modules and CSS-in-JS (discussed in the next section). Scoping CSS to individual components permits Gatsby developers to write traditional CSS that is portable and more maintainable. Styles that only apply to a single component will never infiltrate a different component or change how other components are styled.
CSS Modules is a well-known way to write component-scoped CSS. In short, an individual CSS Module is a CSS stylesheet where all class names (and animation names) are scoped locally out of the box. In CSS Modules, CSS styles become JavaScript objects that are passed between components. Because CSS Modules automatically assign unique names to classes and animations, there is no risk of incurring a collision between two identically named selectors across discrete components.
To illustrate this, let’s return to the example in the previous section—a case of traditional CSS being imported into a Gatsby component—and convert it into a CSS Module being imported into the same Gatsby component. All we need to do to our CSS file is rename it and move it to src/components/footer.module.css.
Also, for the purposes of this look at CSS Modules, let’s select a class name rather than a <div>
element:
/* src/components/footer.module.css */
.footer
{
margin
:
0
auto
;
max-width
:
960px
;
padding
:
0
1.5rem
;
background-color
:
blue
;
}
Now, modify the code in src/components/footer.js to use the CSS Modules paradigm instead of a direct import of the CSS stylesheet:
import
React
from
"react"
import
footerStyles
from
"./footer.module.css"
export
default
function
Footer
({
children
})
{
return
(
<
div
className
=
{
footerStyles
.
footer
}
>
{
children
}
<
/div>
)
}
Note the syntax in our className
value that allows us to drill down into our footer styles and find the specific class name we’re looking for: .footer
. Because we’ve used an import, we can use any of the styles contained in that stylesheet within the component, and no style will ever escape the scope of the component. CSS Modules will render the class name into a dynamic CSS class name such as footer-module--container--2KckL
.
Next, we’ll take a look at how to locally style components with CSS-in-JS.
The Gatsby documentation recommends using CSS Modules if Gatsby developers are interested in the benefits of properly scoping CSS to individual components, as it allows you to take advantage of performance enhancements like dead code elimination and better portability.
CSS-in-JS is a very different approach from CSS Modules in that it focuses on the idea of writing CSS in JavaScript rather than in external CSS files. When using this approach, it’s important to note that some of the traditional syntax of CSS cannot be written in the same way in JavaScript (e.g., backgroundColor
is camelCase
in JavaScript, whereas background-color
in CSS is kebab-case
).
CSS-in-JS is a paradigm that emphasizes scoped styling, dead code elimination, and better performance when dynamic styling gets involved. It has the following characteristics:
While CSS-in-JS is very popular in the JavaScript community to handle CSS, it isn’t so straightforward for those coming from traditional CSS, and it can be time-consuming to refactor traditional CSS into CSS-in-JS and vice versa. And because Gatsby has no opinion about which CSS strategy you use, CSS-in-JS invariably requires the introduction of an additional dependency in the form of a CSS-in-JS library.
There are two CSS-in-JS libraries commonly used with Gatsby, and we’ll cover both of them here: Emotion and Styled Components.
Emotion is a CSS-in-JS library that builds on other preexisting libraries and allows you to add styles to applications using string or object style declarations. Emotion leverages predictable composition of styles to avoid some of the specificity concerns that accompany traditional CSS use. In addition, Emotion uses source maps and labels to offer a better debugging experience and better caching for performance.
In order to use Emotion, we need to install our first plugin, which we’ll do here after setting up another “Hello World” starter:
$
gatsby
new
gtdg-ch2-css-in-js-emotion
gatsbyjs/gatsby-starter-hello-world
Now that we have our site scaffolded, let’s change directories into the site and add the dependencies that are required for Emotion using NPM:
$
cd
gtdg-ch2-css-in-js-emotion
$
npm
install
gatsby-plugin-emotion
@emotion/react
@emotion/styled
Our final step is to open gatsby-config.js and add the plugin to the list of plugins there. If you have no other plugins installed, your gatsby-config.js section for plugins will need to look like the following:
module
.
exports
=
{
plugins
:
[
`gatsby-plugin-emotion`
],
}
Now, let’s introduce styles for a header component into our Emotion page. Note that Emotion prefers to co-locate styles in the same file as the logic defining how those styles will take effect on JSX elements:
import
React
from
"react"
import
styled
from
"@emotion/styled"
import
{
css
}
from
"@emotion/react"
const
Header
=
styled
.
div
`
margin: 0 auto;
max-width: 960px;
padding: 0 1.5rem;
background-color: blue;
color: white;
`
export
default
function
Home
({
children
})
{
return
(
<
Header
>
<
div
>
Welcome
to
my
Gatsby
site
!<
/div>
<
/Header>
)
}
As you can see, here we’re using a multiline string to define the CSS rules for our header.
Emotion also allows us to add arbitrary declarations that can be used to style individual JSX elements. In the following example, we apply a text-transform
property to ensure that the header will be in uppercase on the home page by applying a chunk of CSS to the element:
import
React
from
"react"
import
styled
from
"@emotion/styled"
import
{
css
}
from
"@emotion/react"
const
Header
=
styled
.
div
`
margin: 0 auto;
max-width: 960px;
padding: 0 1.5rem;
background-color: blue;
color: white;
`
const
uppercase
=
css
`
text-transform: uppercase;
`
export
default
function
Home
({
children
})
{
return
(
<
Header
>
<
div
css
=
{
uppercase
}
>
Welcome
to
my
Gatsby
site
!<
/div>
<
/Header>
)
}
When we run gatsby develop
, we can see our newly Emotion-styled Gatsby site, as depicted in Figure 2-1.
One of the most important benefits of Emotion and other CSS-in-JS techniques is the placement of CSS directly within the definition of your component in JavaScript. But you can also use Emotion to apply global styling to your Gatsby site by adding Emotion as a dependency and using it within your layout component. Using Emotion for global styling with a layout component would obviate the need for a separate layout.css file.
Server-side rendering, or the prerendering of HTML before it reaches the client side (see Chapter 3 for more information on this), functions off the shelf in Emotion. You can choose to use React’s renderToString
method or renderToNodeStream
method without any additional configuration. Meanwhile, the extractCritical
feature deletes unused CSS rules (dead code elimination) so pages can load faster down the line.
Styled Components is another CSS-in-JS library that allows Gatsby developers to write actual CSS inside components, just like Emotion. And like Emotion, Styled Components eliminates the possibility of name collisions and dead code appearing in your production CSS.
Let’s take a look at the exact same example we went through with Emotion to see how Styled Components offers a similar approach. In order to use Styled Components, we need to install its plugin, which we’ll do here after setting up yet another “Hello World” starter:
$
gatsby
new
gtdg-ch2-css-in-js-styled-components
gatsbyjs/gatsby-starter-hello-world
With our site scaffolded, let’s change directories into the site and add the dependencies that are required for Styled Components using NPM:
$
cd
gtdg-ch2-css-in-js-styled-components
$
npm
install
gatsby-plugin-styled-components
styled-components
babel-plugin-styled-components
Next, we’ll open gatsby-config.js and add the plugin to the list of plugins there. If you have no other plugins installed, your gatsby-config.js section for plugins will look like the following:
module
.
exports
=
{
plugins
:
[
`gatsby-plugin-styled-components`
],
}
Our index.js will look similar to in the Emotion example and produce the same result, as seen in Figure 2-2:
import
React
from
"react"
import
styled
from
"styled-components"
const
Header
=
styled
.
div
`
margin: 0 auto;
max-width: 960px;
padding: 0 1.5rem;
background-color: blue;
color: white;
`
export
default
function
Home
({
children
})
{
return
(
<
Header
>
<
div
>
Welcome
to
my
Gatsby
site
!<
/div>
<
/Header>
)
}
Just like with Emotion, it’s possible to apply global styles to our layout component, but Styled Components takes a slightly different tack. Styled Components offers a createGlobalStyle
method that defines globally scoped styles. This method should be invoked within Gatsby’s layout component to ensure it’s shared across all pages using that layout component.
Here’s an example of a layout component that uses a GlobalStyle
object, adapted from the Gatsby documentation. Note here that according to our layout component’s state, we are toggling between two different background colors:
import
React
from
"react"
import
{
createGlobalStyle
}
from
"styled-components"
const
GlobalStyle
=
createGlobalStyle
`
body {
background-color:
${
props
=>
(
props
.
theme
===
"blue"
?
"blue"
:
"white"
)
}
;
}
`
export
default
function
Layout
({
children
})
{
return
(
<
React
.
Fragment
>
<
GlobalStyle
theme
=
"blue"
/>
<
/React.Fragment>
)
}
Both Emotion and Styled Components are commonly found in Gatsby implementations. But it’s perfectly fine to use global styling and traditional CSS as well, and Gatsby makes no assumptions about which paradigm of CSS you prefer to use with your site build!
As you probably noticed, we just installed our first two Gatsby plugins. Our next step is to explore Gatsby’s plugin ecosystem and how to extend the core functionality of the Gatsby framework with community plugins.
In Gatsby, plugins are Node.js packages that introduce additional functionality that implements Gatsby’s APIs (Gatsby functionality that developers can hook into to implement their own custom code). Plugins are particularly useful because they can help you modularize functionality into tightly scoped chunks of logic. Gatsby plugins run the gamut of use cases, including search engine optimization (SEO), responsive image support, Sass and LESS support for CSS, offline support, sitemaps and RSS feeds, TypeScript and CoffeeScript compilation, Google Analytics embeds, and more.
You can also create your own custom plugins, which we’ll cover in Chapter 9.
As you saw when we installed the Emotion and Styled Components plugins, the authoritative way to install a new Gatsby plugin is to use the NPM installation process to add the dependency to your package.json file. However, Gatsby also requires you to add the plugin explicitly to gatsby-config.js so Gatsby’s aware of it too.
There are several broad categories of plugins that exist in the Gatsby ecosystem:
There are also many plugins that don’t fit into a clear category. To get an idea of what’s available, check out the Gatsby Plugin Library.
Themes are also a type of Gatsby plugin, but they require a full discussion in their own right. We’ll return to them in Chapter 10.
It’s also important to note where plugins are not necessary. Though plugins introduce additional functionality to Gatsby sites, not all additional functionality needs to be added via a Gatsby plugin. For instance, if you wish to use JavaScript packages that provide developer utilities, like lodash
or axios
, or that integrate libraries that are essential to the functioning of a component, like d3
for visualization, it’s generally best to import the package directly as a dependency in package.json rather than as a plugin. This means that you would install the library normally as an NPM package but refrain from editing anything in gatsby-config.js to reflect the new library’s presence.
As a general rule, to help you decide whether some piece of functionality should be a plugin or a separate library, if the functionality does not hook into or implement Gatsby’s APIs in any way, it’s generally safe to import it solely as a dependency and not write or search for a plugin that contains it. In some cases, however, you can choose between an existing Gatsby plugin and a library: for example, you could use either gatsby-plugin-styled-components
, which offers a deeper integration with Gatsby APIs, or the styled-components
package itself, which you would need to implement yourself in Gatsby where you wish to use it.
Recall from our examination of Emotion and Styled Components how the installation process for plugins works. Let’s revisit it here with a typical plugin. To keep things simple, we’ll use gatsby-plugin-google-analytics
, which allows us to embed Google Analytics code into our Gatsby site.
You can install a plugin from the root directory of your Gatsby site, and you can install a plugin at any time—at the very start of the development process when your site has just been scaffolded, or well into the project timeline when your site is full of other installed plugins. To install a plugin, run this command, substituting the plugin name for a name of your choosing:
$
npm
install
gatsby-plugin-google-analytics
In your gatsby-config.js file, ensure that your plugin is represented in the plugins
array. If you’re adding a plugin for the first time, you may need to add the plugins
array yourself. If you already have plugins in your configuration, you can simply add it as a member to the existing array:
module
.
exports
=
{
plugins
:
[
`gatsby-plugin-google-analytics`
],
}
You’ll follow this exact process every single time you need to introduce another plugin into your Gatsby site.
Some plugins also accept options in their definitions in gatsby-config.js. This is usually to configure key parameters specifying how the plugin should behave or credentials that are required for an external service. For instance, a typical options
object for gatsby-plugin-google-analytics
looks like this:
module
.
exports
=
{
plugins
:
[
{
resolve
:
`gatsby-plugin-google-analytics`
,
options
:
{
// The property ID; the tracking code won't be generated without it
trackingId
:
"YOUR_GOOGLE_ANALYTICS_TRACKING_ID"
,
// Defines where to place the tracking script - `true` in the head
// and `false` in the body
head
:
false
,
// Setting this parameter is optional
anonymize
:
true
,
// Setting this parameter is also optional
respectDNT
:
true
,
// Avoids sending pageview hits from custom paths
exclude
:
[
"/preview/**"
,
"/do-not-track/me/too/"
],
// Delays sending pageview hits on route update (in milliseconds)
pageTransitionDelay
:
0
,
// Enables Google Optimize using your container Id
optimizeId
:
"YOUR_GOOGLE_OPTIMIZE_TRACKING_ID"
,
// Enables Google Optimize Experiment ID
experimentId
:
"YOUR_GOOGLE_EXPERIMENT_ID"
,
// Set Variation ID. 0 for original 1,2,3....
variationId
:
"YOUR_GOOGLE_OPTIMIZE_VARIATION_ID"
,
// Defers execution of google analytics script after page load
defer
:
false
,
// Any additional optional fields
sampleRate
:
5
,
siteSpeedSampleRate
:
10
,
cookieDomain
:
"example.com"
,
},
},
],
}
Depending on the nature of your plugin, it may or may not require options.
It’s also possible to load plugins from your local filesystem, in much the same way you might derive data from the filesystem. Gatsby is able to load plugins from your Gatsby codebase as long as you place them in a top-level plugins directory alongside the src directory. Local plugins are valuable for cases where you may need to introduce proprietary code to a Gatsby codebase or you wish to write your own custom plugins.
The Gatsby documentation provides an example of a project structure that would leverage a local plugin called gatsby-local-plugin
:
/my-gatsby-site └── /src └── /pages └── /components └── /plugins └── /gatsby-local-plugin └── /package.json └── /gatsby-node.js └── gatsby-config.js └── gatsby-node.js └── package.json
You can add any number of local plugins to this folder, as long as they all have distinct names.
With local plugins, you don’t need an NPM installation command, but you do need to configure the plugin in gatsby-config.js so that Gatsby is aware of it:
module
.
exports
=
{
plugins
:
[
`gatsby-local-plugin`
]
}
To verify your plugin is loading correctly in your Gatsby site, you can add a gatsby-node.js file to the plugins directory (we’ll cover what this file does in Chapter 6) and add a line to the terminal output that is printed when you run either gatsby develop
or gatsby build
. This line should use one of Gatsby’s build hooks, such as onPreInit
(before any other step), which we’ll also discuss in more detail in Chapter 6:
exports
.
onPreInit
=
()
=>
{
console
.
log
(
"Hey, I’m a local plugin that just got loaded! Look at me!"
)
}
In your terminal output, the next time you run gatsby develop
or gatsby build
, you will see the following:
success open and validate gatsby-configs - 0.053s success load plugins - 1.036s Hey, I’m a local plugin that just got loaded! Look at me! success onPreInit - 0.025s ...
You can also load a local plugin from somewhere outside the plugins folder. I’ll cover that approach when we look at writing custom plugins in Chapter 9.
In this chapter, we examined several of the most important foundational concepts in Gatsby in detail in order to prepare us for an examination of common use cases in Chapter 3. In addition, we inspected the Gatsby CLI commands you’ll use most frequently in the day-to-day work of implementing Gatsby, whether that involves spinning up starters quickly, facilitating the use of CSS-in-JS libraries, or installing new plugins.
We also explored the structure of Gatsby projects in terms of pages and components to support your journey into new Gatsby implementations. Because CSS is a baseline requirement for websites today, we also walked through the many possible ways to introduce CSS to your Gatsby site.
In the next chapter, we’ll build on this conceptual foundation and begin to work with common use cases to build certain functionality into a boilerplate Gatsby site, including analytics, form handling, contact forms, search engine optimization, 404 pages, and other common requirements in web development today. In the process, we’ll find practical applications for all of the underpinnings we covered in this chapter.
44.193.208.105