Welcome back to "Building a GatsbyJS Blog"! In this second part of the series, we'll focus on creating the Layout and pages for our blog. We'll start by creating a reusable component, the "Layout," which we'll use on all our pages to ensure a consistent design. Next, we'll make the pages for our blog and add some primary content. While we won't be adding the blog post pages just yet, we'll lay the groundwork for them in the next series post. So, let's get started with "Building a GatsbyJS Blog: Layout and Pages." Table of Contents Navigation and Routing Layout Pages Conclusion Navigation and Routing To begin with, though. Let's do some preparation. Define the navigation routes First, we will define our navigation routes in the section. This will help us later when we want to ensure our navigation links are uniform and have a single source of truth. gatsby-config.ts siteMetadata The section of your config is an excellent place to store any information about the site to which you will want easy access. Contact info, social media accounts, and navigation route definitions are perfect examples of things to store there. siteMetadata Open up your file and add the following code to the section: gatsby-config.ts siteMetadata siteMetadata: { title: `My Gatsby Blog`, siteUrl: `https://www.yourdomain.tld`, // ↓ Add this section navigation: [ { name: 'About', path: '/about', }, { name: 'Blog', path: '/blog' } ] }, Create the hook. useMetadata We will often refer to the site metadata, so it's a good idea to have a reusable utility to quickly gather the information. We are going to do that by making use of a custom React hook we'll call . useMetadata Create a new folder and file called src/hooks/use-site-metadata.ts mkdir src/hooks touch src/hooks/use-site-metadata.ts Then, add the following content to the file: // src/hooks/use-site-metadata.ts import { graphql, useStaticQuery } from 'gatsby'; export const useSiteMetadata = () => { const data = useStaticQuery<{ site: { siteMetadata: Queries.SiteSiteMetadata }; }>(graphql` query SiteMetadataQuery { site { siteMetadata { title siteUrl navigation { name path } } } } `); return data.site.siteMetadata; }; This custom hook can be added to any page or component in our codebase, making accessing the site's information easy. Of course, as we add more metadata, we'll need to update this query. You may prefer the file name ; I've settled into a specific naming convention and folder structure for all my TypeScript projects, and your mileage may vary. Feel free to modify this to whatever you prefer. No judgment! 😊 useSiteMetadata.ts : You'll need to restart the development server for the site metadata to be recognized and for the TypeScript types to be generated correctly. NOTE Create the Header component The Layout component we will create will have a header with a navigation section. We should build that component now, so we have it ready when we want to use it in the Layout. Create a new file for the page header: mkdir ./src/components/ touch ./src/components/PageHeader.tsx Add the following content to that file: // src/components/PageHeader.tsx import { Link } from 'gatsby'; import { StaticImage } from 'gatsby-plugin-image'; import React from 'react'; import { useSiteMetadata } from '../../hooks/use-site-metadata'; export const PageHeader: React.FC = () => { const { title, navigation } = useSiteMetadata(); return ( <header className="bg-white"> <div className="container mx-auto flex max-w-5xl items-center justify-between py-6"> {title && ( <Link to="/" className="flex items-center gap-2 px-4 sm:w-auto sm:pb-0 lg:px-0" > <StaticImage src="../../images/icon.png" alt={title} layout="fixed" height={35} width={35} /> <span className="whitespace-nowrap text-xl font-bold uppercase"> {title} </span> </Link> )} <nav className="flex w-full items-center justify-between text-sm sm:w-auto"> <ul className="flex justify-between px-2 sm:px-0"> {navigation?.map( (nav) => nav?.path && ( <li key={nav.path} className="px-2 sm:px-4"> <Link to={nav.path}>{nav.name}</Link> </li> ) )} </ul> </nav> </div> </header> ); }; This defines the component we'll use for all our blog pages. In the next section, we will create a component for our pages. PageHeader Footer Create the Footer component We want a footer on each page with basic information about our blog. Right now, it's going to be sparse, but we might add to it later. Create the file for the footer: touch ./src/components/PageFooter.tsx And add the following content to that file: // src/components/PageFooter.tsx import { Link } from 'gatsby'; import { StaticImage } from 'gatsby-plugin-image'; import React from 'react'; import { useSiteMetadata } from '../../hooks/use-site-metadata'; export const PageFooter: React.FC = () => { const { title, navigation } = useSiteMetadata(); return ( <footer className="bg-purple-200"> <div className="container mx-auto flex max-w-5xl flex-col justify-evenly py-12 sm:flex-row"> <div className="mb-12 sm:mb-0"> {title && ( <div className="mb-4 flex items-center justify-center gap-4 sm:-ml-6"> <StaticImage className="inline-block" layout="fixed" height={20} width={20} src="../../images/icon.png" alt={title} /> <span className="text-xl font-bold uppercase text-black"> {title} </span> </div> )} </div> <div className="text-center sm:text-left"> <ul className="flex flex-col items-center gap-4 sm:flex-row"> {navigation?.map( (nav) => nav?.path && ( <li key={nav.path}> <Link className="underline" to={nav.path}> {nav.name} </Link> </li> ) )} </ul> </div> </div> </footer> ); }; Notice that we pull the navigation from . This is an example of how having that site metadata readily available can lead to better consistency across our pages. siteMetadata In the next section, we'll create a component and add the and . PageLayout PageFooter PageHeader Layout One of the keys to a successful blog is creating a sense of cohesion and continuity between pages. That's why in this part of the series, we will make a "Layout" component that will provide a consistent design across all our pages. The "Layout" component will consist of a header with navigation and a footer at the bottom. Create the PageLayout component As before, start by creating the file: touch ./src/components/PageLayout.tsx Then add this content to the file: // src/components/PageLayout.tsx import React from 'react'; import { PageFooter } from '../page-footer'; import { PageHeader } from '../page-header'; export const PageLayout: React.FC<React.PropsWithChildren> = ({ children }) => { return ( <main className="font-sans font-light"> <PageHeader /> <div className="mx-auto mb-12 max-w-5xl">{children}</div> <PageFooter /> </main> ); }; Now we have a layout for all our pages! Notice that each page will be wrapped in a tag, where we set the default font family and weight. You can add other defaults here as well. <main> Pages It's time to start making some pages. First, we will edit the home page and create the "About" page. After that, the "Blog" page is a little more complex, so we will get to the "Blog" page in another post. The Home page To start, open up in your editor and erase everything already there. We don't like it. Then add this: ./src/pages/index.tsx // src/pages/index.tsx import type { HeadFC, PageProps } from 'gatsby'; import * as React from 'react'; import { PageLayout } from '../components/page-layout'; const IndexPage: React.FC<PageProps> = () => { return ( <PageLayout> <div className="container mx-auto px-4 lg:px-0"> <h1 className="mb-8 text-4xl font-bold lg:text-5xl">My Gatsby Blog</h1> <span className="text-lg">This is my Gatsby Blog home page!</span> </div> </PageLayout> ); }; export default IndexPage; export const Head: HeadFC = () => <title>Home Page</title>; If you reload the page, you should see something like this: The About page We want to do something very similar for the "About" page. Create the new page like so: touch ./src/pages/about.tsx : Any file inside the pages directory automatically becomes a page with a route corresponding to the file name and folder structure above it. TIP Then add the following content: import { graphql, HeadFC, PageProps } from 'gatsby'; import { GatsbyImage, getImage } from 'gatsby-plugin-image'; import * as React from 'react'; import { PageLayout } from '../components/page-layout'; const AboutPage: React.FC<PageProps<Queries.AboutPageQuery>> = ({ data }) => { const profileImage = data.profileImage ? getImage(data.profileImage.childImageSharp) : null; return ( <PageLayout> <div className="container mx-auto px-4 lg:px-0"> <h1 className="mb-8 text-4xl font-bold lg:text-5xl">About Us</h1> <span className="text-lg"> Welcome to our blog's "About" page! We're glad you're here. This page is all about us, the people behind the blog. We're passionate about sharing our knowledge, opinions, and experiences with the world, and we hope you find our content informative and enjoyable. </span> <div className="mt-8 flex flex-col gap-4 sm:flex-row sm:items-center"> {profileImage && ( <div className="w-full sm:w-1/5"> <GatsbyImage image={profileImage} alt="My Profile Image" className="w-full" /> </div> )} <div className="flex-1 text-lg"> Meet John, the tech enthusiast behind our Javascript blog. With over a decade of experience in web development, John has become a seasoned expert in all things Javascript. His passion for coding and his love of sharing knowledge have led him to create a blog that aims to help people master the art of Javascript and create amazing web experiences. </div> </div> </div> </PageLayout> ); }; export default AboutPage; // This query will get run when the page is built, and the data will be passed in // as the `data` property to the exported `AboutPage` above. export const pageQuery = graphql` query AboutPage { profileImage: file(relativePath: { eq: "profile.jpg" }) { childImageSharp { gatsbyImageData(layout: FULL_WIDTH) } } } `; export const Head: HeadFC = () => <title>About Page</title>; : Notice that the "About" page uses the component just like the "Home" page. This gives each page a consistent look and feel without having to duplicate code. NOTE PageLayout The result should be something like this: Conclusion In this post, we made some significant progress toward creating our blog. First, we built a "Layout" component that will serve as the scaffolding for all of our pages, adding navigation elements, a header, and a footer to provide a consistent design across our site. We also created the home page and the "About" page to get a sense of how our blog will look and feel. However, we're just getting started. In the next section of the series, we'll focus on building the blog page and individual post pages. This is where the real magic happens, and we'll be able to start sharing our thoughts and expertise with the world. By the end of the next post, we'll have a fully functional blog with a robust design and Layout. Then, we'll be ready to start creating and sharing content with our readers. Also published here.