How to put post date in Gatsby URL?

All we need is an easy explanation of the problem, so here it is.

All the Gatsby starter demos have a path like /gatsby-starter-blog/hi-folks/

How do I set it up with /2015-05-28/hi-folks/ or just the year with /2015/hi-folks/.


How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Two options:

1) Just put the blog posts in directories named like you want the url to be so in this case /2015-05-28/hi-folks/
2) You can programmatically set paths by exporting a function from gatsby-node.js called rewritePath. It is called for each page with filesystem data for the file the page comes from + the page’s metadata. So say you want to set the date of the post in your markdown’s frontmatter and have each post be a simple markdown file with paths like /

So to do what you want, add to your gatsby-node.js something like:

import moment from 'moment'

exports.rewritePath = (parsedFilePath, metadata) => {
  if (parsedFilePath.ext === "md") {
    return `/${moment(metadata.createdAt).format('YYYY')}/${}/`

Method 2

rewritePath is no longer supported in Gatsby. Here’s a solution confirmed to works in Gatsby 2.3,

const m = moment(
const value = `${m.format('YYYY')}/${m.format('MM')}/${slug}`
createNodeField({ node, name: 'slug', value })

Method 3

In my gatsby-node.js file I added a slug generator and a template resolver.

Inside src/posts I added a folder 2020 inside that folder I create folders that make slug address paths like so my-blog-post and inside those folders, I have an file.

Now I have URL’s that look like

 * Implement Gatsby's Node APIs in this file.
 * See:

const { createFilePath } = require(`gatsby-source-filesystem`);

// Generates pages for the blog posts
exports.createPages = async function ({ actions, graphql }) {
    const { data } = await graphql(`
        query {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
    data.allMarkdownRemark.edges.forEach((edge) => {
        const slug = edge.node.fields.slug;
            path: slug,
            component: require.resolve(`./src/templates/blog-post.js`),
            context: { slug: slug },

// Adds url slugs to the graph data response
exports.onCreateNode = ({ node, getNode, actions }) => {
    const { createNodeField } = actions;
    if (node.internal.type === `MarkdownRemark`) {
        const slug = createFilePath({ node, getNode, basePath: `posts` });
            name: `slug`,
            value: slug,

Inside my src/templates/blog-post.js file I have:

import React from 'react';
import { graphql } from 'gatsby';
import Layout from '../components/layout';

export default function BlogPost({ data }) {
    const post = data.markdownRemark;
    const tags = (post.frontmatter.tags || []).map((tag, i) => {
        return <li key={i}>#{tag}</li>;

    return (
            <article className="post">
                    <div className="meta">
                <section dangerouslySetInnerHTML={{ __html: post.html }} />

export const query = graphql`
    query($slug: String!) {
        markdownRemark(fields: { slug: { eq: $slug } }) {
            frontmatter {
                date(formatString: "DD-MMMM-YYYY")

My markdown files then use frontmatter data like so:

title: Post title here
date: "2020-05-25T14:23:23Z"
description: "A small intro here"
tags: ["tag1", "tag2"]

Main post content would be here...

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from or, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply