visit
NOTE: An image before the main content is a classic look for websites and blogs. But you may decide it's too dated or has lousy SEO, which is totally cool. So I'm not necessarily advocating for this style (although I use it on my own website).
Open up the ./src/components/PageLayout.tsx
file and modify it as such:
// ./src/components/PageLayout.tsx
import { GatsbyImage, IGatsbyImageData } from 'gatsby-plugin-image';
import React from 'react';
import { PageFooter } from '../page-footer';
import { PageHeader } from '../page-header';
interface PageLayoutProps {
image?: IGatsbyImageData | null;
title?: string;
}
export const PageLayout: React.FC<React.PropsWithChildren<PageLayoutProps>> = ({
children,
image,
title,
}) => {
return (
<main className="font-sans font-light">
<PageHeader />
{image && (
<div className="relative mb-12 flex h-96 items-center justify-center">
<GatsbyImage image={image} alt="" className="absolute inset-0" />
{title && (
<div className="z-20 mx-auto max-w-5xl">
<h1 className="text-4xl font-bold text-white sm:text-5xl">
{title}
</h1>
</div>
)}
{/* Darken the background image a little so the text shows up better */}
<div className="absolute inset-0 z-10 bg-gray-900 opacity-30" />
</div>
)}
<div className="mx-auto mb-12 max-w-5xl">{children}</div>
<PageFooter />
</main>
);
};
But before we do that, we will have to find an image and put it in our ./src/images
folder. So go ahead and locate a nice picture. For example, I use for many of my stock images.
Once you find an image, move it over to the ./src/images
directory:
mv /path/to/downloaded/image.jpg ./src/images/header.jpg
// ./src/pages/index.tsx
import { graphql, HeadFC, PageProps } from 'gatsby';
import { getImage } from 'gatsby-plugin-image';
import * as React from 'react';
import { PageLayout } from '../components/page-layout';
const IndexPage: React.FC<PageProps<Queries.IndexPageQuery>> = ({ data }) => {
const image = data.headerImage
? getImage(data.headerImage.childImageSharp)
: null;
return (
<PageLayout image={image} title="My Gatsby Blog">
<div className="container mx-auto px-4 lg:px-0">
<span className="text-lg">This is my Gatsby Blog home page!</span>
</div>
</PageLayout>
);
};
export default IndexPage;
// This query will be run during the GatsbyJS build process when creating our
// pages. The data will be passed to our page component as a `data` property.
export const pageQuery = graphql`
query IndexPage {
headerImage: file(relativePath: { eq: "header.jpg" }) {
childImageSharp {
gatsbyImageData(layout: FULL_WIDTH)
}
}
}
`;
export const Head: HeadFC = () => <title>Home Page</title>;
NOTE: The
relativePath
file name argument needs to match the file name of the file you added to the./src/images
folder. So, double-check that you've got that file name correct.
To get the same result on your "About" page and "Blog List" pages, do the same thing we did above to the .src/pages/about.tsx
and ./src/pages/blog.tsx
page components.
TIP: If you want different images for each page, download additional images and move them to the
./src/images
folder. Then, update thepageQuery
for each page component to gather the correct image data by updating therelativePath
accordingly.
The trick is that we can't define the featured image using a pageQuery
as we did for our main pages since we're using MDX to generate these pages.
This is where the frontmatter for each blog post comes in. We will use the frontmatter section of each MDX file to define a featuredImage
property and use that property to find the image and display it in the blog post.
---
date: 2023-02-18 01:00:00
slug: my-first-blog-post
title: My First Blog Post
author: John Smith
featuredImage: ./featured.jpg
---
NOTE: Make sure to add this to each of your blog posts.
mv /path/to/downloaded/image.jpg .src/content/<blog-directory>/featured.jpg
NOTE: You'll want to ensure you have an image for each of your posts; otherwise, you might see some errors when GatsbyJS is building your queries.
Open up the ./src/templates/BlogPostTemplate.tsx
file and update it as follows:
// ./src/templates/BlogPostTemplate.tsx
import { MDXProvider } from '@mdx-js/react';
import { graphql, PageProps } from 'gatsby';
import { getImage } from 'gatsby-plugin-image';
import React from 'react';
import { components, MainContent } from '../components/mdx-components';
import { PageLayout } from '../components/page-layout';
const BlogPostTemplate: React.FC<PageProps<Queries.BlogPostQuery>> = ({
data,
children,
}) => {
const featuredImage = data.mdx?.frontmatter?.featuredImage
? getImage(data.mdx.frontmatter.featuredImage.childImageSharp)
: null;
return (
<PageLayout
image={featuredImage}
title={data.mdx?.frontmatter?.title ?? undefined}
>
<MainContent>
<div className="mb-8">
<span className="text-sm font-thin">
By {data.mdx?.frontmatter?.author} on {data.mdx?.frontmatter?.date}
</span>
</div>
<MDXProvider components={components}>{children}</MDXProvider>
</MainContent>
</PageLayout>
);
};
export default BlogPostTemplate;
export const query = graphql`
query BlogPost($id: String!) {
mdx(id: { eq: $id }) {
frontmatter {
title
author
date(formatString: "MMMM DD, YYYY")
featuredImage {
childImageSharp {
gatsbyImageData(layout: FULL_WIDTH)
}
}
}
}
}
`;
We will update the query in our ./src/pages/blog.tsx
page component to do this.
// ./src/pages/blog.tsx
import { graphql, Link, PageProps } from 'gatsby';
import { GatsbyImage, getImage } from 'gatsby-plugin-image';
import React from 'react';
import { PageLayout } from '../components/page-layout';
const BlogPage: React.FC<PageProps<Queries.BlogPageQuery>> = ({ data }) => {
const headerImage = data.headerImage
? getImage(data.headerImage.childImageSharp)
: null;
return (
<PageLayout image={headerImage} title="Blog">
<ul className="mx-auto max-w-3xl p-4 sm:p-0">
{data.allMdx.edges.map(({ node }) => (
<li key={node.id} className="mb-4 h-72 last-of-type:mb-0">
<Link
to={`/blog/${node.frontmatter?.slug}`}
className="flex items-center overflow-hidden rounded-lg border border-gray-400"
>
<div className="flex-1 md:h-72 md:w-64">
{node.frontmatter?.featuredImage && (
<GatsbyImage
image={
getImage(node.frontmatter.featuredImage.childImageSharp)!
}
alt=""
className="h-full w-full"
/>
)}
</div>
<div className="flex-1 p-6">
<h2 className="mb-4 text-xl font-bold">
{node.frontmatter?.title}
</h2>
<span className="mb-2 block text-sm font-thin">
By {node.frontmatter?.author} on {node.frontmatter?.date}
</span>
<span className="block text-lg">{node.excerpt}</span>
</div>
</Link>
</li>
))}
</ul>
</PageLayout>
);
};
export default BlogPage;
export const query = graphql`
query BlogPage {
headerImage: file(relativePath: { eq: "header.jpg" }) {
childImageSharp {
gatsbyImageData(layout: FULL_WIDTH)
}
}
allMdx(sort: { frontmatter: { date: DESC } }) {
edges {
node {
id
excerpt(pruneLength: 160)
frontmatter {
title
author
date(formatString: "MMMM DD, YYYY")
slug
featuredImage {
childImageSharp {
gatsbyImageData(layout: FULL_WIDTH)
}
}
}
}
}
}
}
`;