Building a blog and admin dashboard with Netlify CMS and Gridsome

Curating and editing content is a big part of maintaining a site, traditionally this meant using a CMS like WordPress, and in many cases managing databases and servers. With Netlify CMS we can create, edit and publish content without needing to set up databases or connect our sites to those databases via an API or scripts. This method of storing content works well with static site generators and is appealing for projects that will be handed over to a less technical person.

In this tutorial, I will demonstrate how to set up Netlify CMS to manage content that will be rendered via Gridsome.

Here’s a sneak peek of our outcome from the tutorial:

A view of the admin CMS panel
A view of our simple blog

To follow along with this guide, you’ll need Node.js and a package manager (I’ll be using Yarn) installed on your computer. You can install Node by downloading the binaries from the official download page.

After installing Node, we install our package manager Yarn by running:

npm i -g yarn

Setting up our Gridsome app

We first install the Gridsome cli by running:

yarn global add @gridsome/cli

Then we create our new project:

# create gridsome project
gridsome create personal-blog
# install dependencies
cd personal-blog
yarn install
# run new app
yarn run develop

Running our new Gridsome project will show the default first page:

You can find more help with getting your Gridsome app running here

Setting up Netlify CMS

To add Netlify CMS to our Gridsome blog project, first we’ll install the Netlify CMS plugins:

yarn add netlify-cms gridsome-plugin-netlify-cms

Then, we register the installed plugins in the gridsome.config.js file:

module.exports = {   siteName: 'Personal Blog',   ...   plugins: [

use: `gridsome-plugin-netlify-cms`, options: { publicPath: `/admin`, modulePath: `src/admin/index.js` } }, ]}

💡 The plugin options allow us some flexibility in what we name our admin panel and how we use it. You can change your folder name and subsequently publicPath from /admin to whatever else we choose. We also specify modulePath: "src/admin/index.js" to register any customizations to the panel we would like.

Next, to add the CMS pages to the project, we create an admin directory in src:

#navigate to src
cd src
# create the src directory
mkdir admin

Then we create our 3 most important files

  • index.html as the entry point to the admin panel,
  • index.js for the CMS customizations, and
  • config.yml to setup up the structure for storing and entering content for our blog.

In index.html, we’ll create a basic HTML file structure including a script tag to import our index.js file. Here’s an example:

<!doctype html>   <html>      <head>          <meta charset="utf-8">          <meta name="viewport" content="width=device-width,         initial-scale=1.0">          <title>Personal Blog CMS</title>      </head>      <body>          <script src="index.js" type="module"></script>      </body>   </html>

💡 Remember to set type="module" on the script tag where we import index.js this is because our script uses import statement and so must be treated as a module.

In index.js we import and initialize the Netlify CMS module. This file is useful for registering customizations to the CMS such as customized previews. For now, we will only import and initialize the CMS.

import CMS from "netlify-cms"
// Initialize the CMS object

Now we’ll move on to config.yml which is where we set up the structure of our posts and how they will be stored.

Netlify CMS stores data in files and commits those edits straight to the repository so first, we specify the repository to update and track changes using the backend tag:

backend:repo: "ibywaks/****"name: githubbranch: "master" # Branch to update (optional; defaults to master)

Then we set directories for storing and serving media

...media_folder: "static/uploads"public_folder: "/uploads"

💡 If you don’t have these directories in your project you can create them using the mkdir command on your terminal.

Next, we setup the structure for storing our content, this is called the collection section. In this section we specify the collection name, file extension, file or folder naming convention and the data field types indicated as widgets. Here’s what our collection configuration would look like:

...collections:    - label: "Posts"      name: "posts"      extension: "json"      folder: "src/data/posts"      slug: "{{slug}}"      create: true      fields:          - label: "Title"            name: "title"            widget: "string"          - label: "Date"            name: "date"            widget: "date"            format: "MM/YYYY" #optional          - label: "Category"            name: "category"            widget: "select"            options: ["Travel", "Tech", "Food", "Fashion"]            default: "Tech"          - label: "body"            name: "body"            widget: "text"          - label: "Cover Image"            name: "image"            widget: "image"            media_library:               config:                 multiple: false

💡 For this project, we have chosen to save our content as JSON files in a folder (one file for each post) but you can choose other formats such as *.md or storing all the posts in one file. There are also several options for widgets, you can view all the options here.

Now that we’ve set up our CMS, we can create some posts and move on to showing them on our app.

Displaying our folder stored posts on Gridsome

After setting up the admin CMS and saving some content, we have a directory of JSON files containing our posts. We can then parse those JSON files as data into Gridsome’s internal graphql API. We will do this in gridsome.server.js.

First, let’s install fs so we can read the JSON files

yarn add fs

Then, in gridsome.server.js we import and read the data from the folder, parsing it into the graphql API as collections

const fs = require('fs')const { kebabCase } = require('lodash')...const postFiles = fs.readdirSync('./src/data/posts')module.exports = function (api) {   api.loadSource(({ addCollection }) => {       const postsCollection = addCollection({typeName: 'posts'})       postFiles.forEach(postFile => {         try {            const fileData = require(`./src/data/posts/${postFile}`)            postsCollection.addNode({               slug: kebabCase(fileData.title),               ...fileData            })         } catch (error) {            console.log(`unable to read file: ${postFile}`, error)         }       })  })}

💡 To read the data from files in GraphQL we will import the fs library. We’re also using lodash to create slugs for our posts.

Now that all our posts are in the GraphQL API, we will create our home page and single post page. On our home page, we fill out our HTML in the template tag and import our posts using Gridsome’s page-query tag. You can learn more about querying data in Gridsome here

    ...    <div v-for="(post, index) in otherPosts" :key="index" class="card col-md-4">        <g-image :src="post.image" :alt="post.title" class="card-img-top"/>        <div class="card-body">            <h5 class="card-title">{{ post.title }}</h5>            <p class="card-text">{{ post.body }}</p>            <g-link :to="`/posts/${post.slug}`" target="_blank"class="btn btn-primary">View Post</g-link>       </div>    </div>  </div></div></Layout></template><page-query>   query {      allPosts {         edges {            node {               category               date               title               body               image               slug            }         }      }  }</page-query>

On our single post page, we also create our HTML and import our posts data.

           ...           <div class="card-body">              <h5 class="card-title">{{ currentPost.title }}</h5>              <p class="card-text">{{ currentPost.body }}</p>           </div>         </div>       </div>     </div>  </Layout></template><page-query>    query {       allPosts {          edges {             node {                 category                 date                 title                 body                 image                 slug             }          }       }    }</page-query>

In this case, to display the specific post visited we will filter our posts data using the slug from the route. Here’s how:

<script>    export default {       data() {          return {             postSlug: ''          }       },       computed: {          posts() {              return this.$ => edge.node)          },          currentPost() {              // finding the specific post from our array of posts              return this.posts.find(p => p.slug === this.postSlug)           }       },       mounted() {           // reading the slug passed in from the route           const { slug: postSlug } = this.$route.params           this.postSlug = postSlug       }  }</script>


In this tutorial, we’ve covered building a blog and it’s content management using Gridsome and Netlify CMS.

The entire code from this tutorial is available on Github. There’s a lot of flexibility that Netlify CMS affords, feel free to make changes such as file type or content to your admin panel. And if you have questions or cool ideas on what to do with Gridsome or Netlify CMS, please share with me in the comments section!

Software Developer. Sponge. Fashion girl.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store