How to Set Up Gatsby with Sanity.io for Your Personal Blog
27 March 2023I will walk you through the steps of setting up Gatsby with Sanity.io for your personal blog. I will cover everything from installing the necessary plugins to deploying your blog to a hosting service like Netlify. So let's get started!
Initialise project
Create project with Gatsby docs - https://www.gatsbyjs.com/docs/quick-start/.
npm init gatsby
Sanity.io
Follow gatsby-source-sanity plugin - https://www.gatsbyjs.com/plugins/gatsby-source-sanity/.
Rewrite index page:
import * as React from 'react'
function IndexPage(): React.ReactElement {
return <div>Hello world!</div>
}
export default IndexPage
export function Head(): React.ReactElement {
return <title>Home Page</title>
}
Add basic HTML markup.
Create single post page.
import * as React from 'react'
function SinglePost(): React.ReactElement {
return (
<>
<header>
<h2>Site Title</h2>
</header>
<hr />
<main>
<article>
<h1>Post title</h1>
<small>12 September 1998</small>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nemo labore sed voluptatem. Iste reprehenderit cumque autem quisquam exercitationem optio eum, asperiores porro voluptatibus velit rem delectus placeat labore sit praesentium error voluptate harum vitae earum doloremque suscipit! Omnis laboriosam pariatur repellat cupiditate autem?</p>
</article>
</main>
</>
)
}
export default SinglePost
export function Head(): React.ReactElement {
return <title>Single Post Page</title>
}
Add links cross pages.
GraphiQL
Add graphql queries for index page.
import * as React from 'react'
import { Link, type PageProps, graphql, type HeadProps } from 'gatsby'
function NoPosts(): React.ReactElement {
return (
<p>
No posts found. Please create a post in Sanity Studio and publish it to
see it here.
</p>
)
}
function IndexPage({
data: {
allSanityPost: { nodes },
site
}
}: PageProps<Queries.IndexPageQuery>): React.ReactElement {
const posts = nodes !== null && nodes.length > 0 ? nodes : []
if (posts.length === 0) {
return <NoPosts />
}
const featuredPost = {
title: posts[0].title,
description: posts[0].description,
link:
posts[0].slug?.current !== undefined && posts[0].slug.current !== null
? `/posts/${posts[0].slug.current}`
: '',
readMore:
posts[0].readMore === null ? 'Continue reading...' : posts[0].readMore
}
const otherPosts = posts.slice(1).map(post => ({
title: post.title,
description: post.description,
link:
post.slug?.current !== undefined && post.slug.current !== null
? `/posts/${post.slug.current}`
: '',
readMore: post.readMore === null ? 'Continue reading...' : post.readMore
}))
const siteMetadata = site?.siteMetadata
return (
<>
<header>
<h1>{siteMetadata?.title}</h1>
<p>{siteMetadata?.description}</p>
</header>
<hr />
<main>
<article>
<h2>{featuredPost.title}</h2>
<p>{featuredPost.description}</p>
{featuredPost.link.length > 0 && (
<p>
<Link to={featuredPost.link}>{featuredPost.readMore}</Link>
</p>
)}
</article>
{otherPosts.length > 0 && (
<ul>
{otherPosts.map(post => (
<li key={post.title}>
<article>
<h3>{post.title}</h3>
<p>{post.description}</p>
{post.link.length > 0 && (
<p>
<Link to={post.link}>{post.readMore}</Link>
</p>
)}
</article>
</li>
))}
</ul>
)}
</main>
</>
)
}
export default IndexPage
export function Head({
data: { site }
}: HeadProps<Queries.IndexPageQuery>): React.ReactElement {
const siteMetadata = site?.siteMetadata
return (
<>
<title>{siteMetadata?.title}</title>
<body className="prose lg:prose-xl font-serif mx-8 my-4" />
</>
)
}
export const query = graphql`
query IndexPage {
allSanityPost {
nodes {
title
readMore
slug {
current
}
description
publishedAt(formatString: "DD MMMM YYYY")
}
}
site {
siteMetadata {
title
description
}
}
}
`
Dynamic Pages
import { type CreatePagesArgs, type GatsbyNode } from 'gatsby'
import path from 'path'
async function turnArticlesIntoPages({
graphql,
actions
}: CreatePagesArgs): Promise<void> {
// 1. Get a template for this page
const postTemplate = path.resolve('./src/pages/single-post.tsx')
// 2. Query all posts
const { data } = await graphql<Queries.AllPostsQuery>(`
query AllPosts {
allSanityPost(sort: { publishedAt: DESC }, limit: 100) {
nodes {
title
slug {
current
}
}
}
}
`)
const posts =
data?.allSanityPost.nodes !== undefined &&
data.allSanityPost.nodes !== null &&
data.allSanityPost.nodes.length > 0
? data.allSanityPost.nodes
: []
// 3. Loop over each post and create a page for that post
posts.forEach(post => {
console.log('Creating page for Article:', post.title)
const link =
post.slug?.current !== undefined && post.slug.current !== null
? post.slug.current
: ''
if (link.length > 0)
actions.createPage({
path: `posts/${link}`,
component: postTemplate,
context: {
slug: link
}
})
})
}
export const createPages: GatsbyNode['createPages'] =
async function createPages(params) {
// Create pages dynamically
await turnArticlesIntoPages(params)
}
Plugins
- Parse Portable Text - https://www.npmjs.com/package/@portabletext/react
- Highlight Code samples - https://www.npmjs.com/package/react-refractor
- Style site with TailwindCSS - https://tailwindcss.com/docs/guides/gatsby
- Update gatsby-browser.ts with styles
import './src/global.css'
import 'prismjs/themes/prism.css'
GitHub
- Create a repository - https://github.com/new.
- Connect repo and local folder:
git remote add origin path-to-repo.git
. - Start new branch:
git checkout -b feature/gatsby
. - Commit
Initial commit
.
Linting
- Lint files with ESLint:
npm init @eslint/config
. - Format with Prettier:
npm install --save-dev --save-exact prettier
. - Create config file:
echo {}> .prettierrc.json
. - Bind prettier with eslint:
npm install --save-dev eslint-config-prettier
.
{
"printWidth": 80,
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "avoid",
"proseWrap": "always"
}
{
"extends": [
"some-other-config-you-use",
"@sanity/eslint-config-studio",
"prettier"
]
}
- Point TypeScript config file in parserOptions.project.
{
"format": "prettier --write schemas/**/*.ts",
"lint": "eslint --fix schemas/**/*.ts",
"test": "npm run format && npm run lint"
}
- Commit
Add linting
.
Pre-commit hook
Lint files before commiting them with husky: npx husky-init && npm install
.
Commit Add pre-commit hook
.
Deploy
Just use Netlify.
Git issue
If the problem is "main and master are entirely different commit histories.", the following will work.
git checkout feature/gatsby
git branch main feature/gatsby -f
git checkout main
git push origin main -f