How to Setup a Blog with Gatsby, TailwindCSS, and Markdown
Creating a blog today is easier than ever, thanks to the power of modern web development tools. In this guide, we'll explore how to set up a blog using Gatsby, a React-based static site generator, alongside TailwindCSS for styling and Markdown for content management.
1. Create a Template for Blog Posts
To render each blog post, we need a dedicated template that fetches and displays the content. Using Gatsby’s GraphQL layer, we can pull the necessary data from our Markdown files.
Here's a simple Gatsby page component for our blog posts:
import React from "react"
import { graphql, PageProps } from "gatsby"
import Layout from "../components/Layout"
import Section from "../components/Section"
import SEO from "../components/SEO"
import { BlogPostData } from "../types/blog.ts"
export default function BlogTemplate({ data }: { data: BlogPostData }) {
const { markdownRemark } = data
const { frontmatter, html } = markdownRemark
return (
<Layout>
<Section>
<div className="prose mx-auto">
<h1>{frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{ __html: html }} />
</div>
</Section>
</Layout>
)
}
export const pageQuery = graphql`
query ($slug: String!) {
markdownRemark(frontmatter: { slug: { eq: $slug } }) {
html
frontmatter {
categories
date
slug
title
short_description
}
}
otherBlogPosts: allMarkdownRemark(
filter: {
fileAbsolutePath: { regex: "/blog/" }
frontmatter: { slug: { ne: $slug } }
}
sort: { frontmatter: { date: DESC } }
) {
edges {
node {
frontmatter {
categories
date
slug
title
short_description
}
}
}
}
}
`
The BlogTemplate
function receives the data fetched by the GraphQL query and uses it to render the content of a blog post.
2. Use Markdown for Blog Posts
With Gatsby, content can be stored in Markdown format, which is both human-readable and easy to write. The "frontmatter" section at the top of each file provides metadata about the post (like title, categories, and date).
Here's an example of what the beginning of a Markdown blog post might look like:
---
title: "Example Blog Post"
categories: ["Example", "Blog", "Post"]
date: 03.07.2023
short_description: "This is an example blog post."
slug: /blog/example-blog-post
---
# This is an Example Blog Post
Some text here.
The main content of the blog post follows the frontmatter, written in Markdown, which Gatsby will convert to HTML.
3. Integrate Blog Posts in gatsby-node.js
Gatsby uses the gatsby-node.js
file to create pages programmatically. This means you can fetch your blog posts and create a page for each one.
Here's a snippet that does just that:
exports.createPages = async ({ actions, graphql, reporter }) => {
const { createPage } = actions
const blogItemTemplate = require.resolve(
`./src/templates/blogItemTemplate.tsx`
)
const blogResult = await graphql(`
{
allMarkdownRemark(
filter: { fileAbsolutePath: { regex: "/blog/" } }
sort: { frontmatter: { date: DESC } }
) {
edges {
node {
frontmatter {
categories
date
slug
title
}
html
}
}
}
}
`)
// Handle errors
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
blogResult.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.frontmatter.slug,
component: blogItemTemplate,
context: {
// additional data can be passed via context
slug: node.frontmatter.slug,
},
})
})
}
This code fetches all the Markdown files in a "blog" directory, then creates a page for each one using the blog post template we defined earlier.
4. Create an Overview Page to Display All Blog Posts
It's common to have an overview or index page where visitors can see a list of all your blog posts. The code below achieves this by fetching all the blog posts and displaying their titles, with each title being a link to the full post:
import { graphql, Link } from "gatsby"
import React from "react"
import Layout from "../components/Layout"
import Section from "../components/Section"
import { H1 } from "../components/text/H1"
import { BlogPostData } from "../types/blog.ts"
const Blog = ({ data }: { data: BlogPostData }) => {
const posts = data.allMarkdownRemark.edges.map(
(edge: any) => edge.node.frontmatter
)
return (
<Layout>
<Section>
<H1 text="Blog" />
<div className="mt-10 space-y-16 border-t border-gray-200 pt-10 sm:mt-16 sm:pt-16">
{posts.map((post: any) => (
<article
key={post.id}
className="flex max-w-xl flex-col items-start justify-between"
>
<div className="flex items-center gap-x-4 text-xs">
<time dateTime={post.datetime} className="text-gray-500">
{post.date}
</time>
{post.categories.map((category: string) => (
<button className="relative z-10 rounded-full bg-gray-50 px-3 py-1.5 font-medium text-gray-600 hover:bg-gray-100">
{category}
</button>
))}
</div>
<div className="group relative">
<h2 className="mt-3 text-lg font-semibold leading-6 text-gray-900 group-hover:text-gray-600">
<Link to={post.slug}>
<span className="absolute inset-0" />
{post.title}
</Link>
</h2>
<p className="mt-5 line-clamp-3 text-sm leading-6 text-gray-600">
{post.description}
</p>
</div>
</article>
))}
</div>
</Section>
</Layout>
)
}
export default Blog
export const Head = () => {
return <SEO />
}
export const pageQuery = graphql`
allMarkdownRemark(
filter: { fileAbsolutePath: { regex: "/blog/" } }
sort: { frontmatter: { date: DESC } }
) {
edges {
node {
frontmatter {
categories
date
slug
title
short_description
}
}
}
}
}
`
This Blog
component maps over all the posts and displays them in a styled manner using TailwindCSS classes. The page then gets exported and can be added to your site’s navigation.
Conclusion: With just these four steps, you've got the foundation of a Gatsby blog! Gatsby’s ecosystem and plugins, combined with the power of TailwindCSS and the simplicity of Markdown, make for a compelling blogging platform. Whether you're a seasoned developer or just starting out, this setup provides a solid, fast, and stylish base for sharing your thoughts with the world.
Lassen Sie uns zusammenarbeiten
Gerne können wir in einem Telefonat besprechen, wie wir zusammenarbeiten könnten, um Ihr Projekt zu realisieren.
Kontaktieren Sie mich, um loszulegen