Static website using Gatsby and Markdown

Gatsby and NextJS are popular alternatives to create static websites using React. This tutorial shows how to work with Gatsby: use CSS modules to style, add a new page, navigate between pages, create a custom layout, add a plugin, process markdown files, use GraphQL and create custom pages.

Sample App Overview

The sample app is available in Netlify. There is a Home page which has a background image and a link to Services page. Services page lists all the services that the company provides. The user can navigate to the respective service page by clicking on the link. Each service offering is available as a markdown file which gets dynamically converted to a static page.

I am using Yarn as the package manager. Please feel free to use NPM and type the equivalent commands when using NPM. And if you have not installed Gatsby, install it.

yarn global add gatsby-cli

Create a new project

Create a new project using gatsby-cli.

gatsby new gatsbydemo

Using this command creates a project using the default starter project. It is equivalent to the following command.

gatsby new gatsbydemo
https://github.com/gatsbyjs/gatsby-starter-default

There are several other starter templates. To use a different starter template, type the github repo of the starter template after the project name like so.

gatsby new blogdemo
https://github.com/gatsbyjs/gatsby-starter-blog

We will use the default starter template. So change directory to gatsbydemo.

cd gatsbydemo

To start the app, use:

gatsby develop

Open a new website localhost:8000 on the browser.

Style the page using CSS modules

Gatsby considers each component in src/pagesfolder as a page. The component in index.js represents the home page. Change the index page with the following code.

import React from 'react';
import SEO from '../components/seo';

const IndexPage = () => (
    <div>
        <SEO title="Vijay Consulting Services" keywords={[`gatsby`, `application`, `react`]} />
        <div>
            <div>
                <div>
                    Vijay Consulting Services
                </div>
                <p>We offer custom application development using React framework</p>
                <button>View our services</button>
            </div>
        </div>
    </div>
);

export default IndexPage;

The new page does not have a Layout component. But it uses the SEO component. SEO component is a wrapper around React Helmet component. It outputs meta tags into the HTML page. Below the SEO component, we have the page layout. Our home page is a simple company page which offers React development services to its clients. Let’s style the home page. For styles, we use CSS modules.

In Gatsby, any file which has the module.css extension is bundled as a CSS module. CSS module ensures that class names are distinct. Create a new file index.module.css in the pages folder.

.home {
    width: 100%;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    background: url('../images/bg.jpeg') no-repeat center center fixed;
    background-size: cover;
    color: white;
}

.center {
    text-align: center;
}

.company {
    font-size: 36px;
}

.offering {
    font-size: 24px;
    margin-top: 48px;
}

.button {
    background-color: rebeccapurple;
    padding: 10px 20px;
    font-size: 20px;
    border: none;
    outline: none;
}

.button a {
    color: white;
    text-decoration: none;
}

The class home stretches the page and fills the entire browser window. It places the child with center class in the center of the page. Within the center class, there is the company name styled using companyclass, paragraph styled using offering class and button styled using button class. To use these classnames, import the CSS module.

import styles from './index.module.css';

Modify the page content to use these styles.

<div className={styles.home}>
    <div className={styles.center}>
        <div className={styles.company}>Vijay Consulting Services</div>
        <p className={styles.offering}>
            We offer custom application development using React framework
        </p>
        <button className={styles.button}>
            View our services
        </button>
    </div>
</div>

Place a background image in the images folder for the code to work. Gatsby uses Webpack which bundles the image appropriately. This is how the home page should look.

Home page

Create a new page

Creating a new page in Gatsby is quite simple. Just add another component to the pages folder. Create the file, services.js:

import React from 'react';
import Layout from '../components/layout';
import styles from './services.module.css';

export default function Services() {
    return (
        <Layout>
            <div>
                <h1>Services</h1>
                <p>View our service offerings:</p>
                <ul className={styles.services}>
                    <li>React apps</li>
                    <li>React Native apps</li>
                    <li>Next apps</li>
                    <li>Gatsby apps</li>
                </ul>
            </div>
        </Layout>
    )
}

Services page uses the Layout component and uses another CSS module to style the menu. Create a new file, service.module.css:

.services {
    margin-top: 24px;
    font-size: 16px;
}

.services a:link, .services a:visited {
    color: rgba(0,0,0,.56);
    text-decoration: none;
}

.services a:hover {
    color: black;
    text-decoration: underline;
}

On the browser, navigate to the new page by typing localhost:8000/services. Next, we will link the Home page to the Services page.

Navigating between pages

The Link component in Gatsby allows navigation between pages. To allow the button on the home page to navigate to the newly created services page, add the following code in index.js.

<button className={styles.button}>
    <Link to="/services">View our services</Link>
</button>

Import the Link component at the top of the file.

import { Link } from 'gatsby';

Clicking on the button should navigate back to the services page. But how do we move back to the Home page? Let’s modify the Header component which is part of the Layout component in the components folder.

Modifying the Layout

Add the following code below the h1 tag in the Header component (src/components/header.js).

<div>
    <ul className={styles.menu}>
        <li>
            <Link to="/">Home</Link>
        </li>
        <li>
            <Link to="/services">Services</Link>
        </li>
    </ul>
</div>

Add a CSS module, header.module.css:

.menu {
    list-style: none;
    margin: 24px 0 0 0;
}

.menu li {
    display: inline;
    margin-right: 16px;
}

.menu li a {
    color: #e0e0e0;
    text-decoration: none;
}

.menu li a:hover {
    color: white;
    text-decoration: underline;
}

The above styles make the menu within the header look better. Import the module in the header component.

import styles from './header.module.css';

With the above changes, we can navigate between the Home page and the Services page easily. This is how the Services page should look now.

Services page

Reading files from the filesystem

Create a new services folder with the markdown files from the github repo for this article.

Gatsby uses the plugin mechanism for processing data. This includes the files in the local filesystem. All the plugins are available in gatsby-config.js. The starter template has few plugins included. One of them is gatsby-plugin-react-helmet for inserting meta tags. There is another plugin for processing image files in the images folder.

{
    resolve: `gatsby-source-filesystem`,
    options: {
        name: `images`,
        path: `${__dirname}/src/images`
    }
}

Use the same plugin for reading markdown files from the services folder.

{
    resolve: `gatsby-source-filesystem`,
    options: {
        name: `services`,
        path: `${__dirname}/src/services`
    }
},

Add a new plugin

There are several plugins available. Install the remark plugin for processing markdown files.

yarn add gatsby-transformer-remark

Add the plugin to the config file, gatsby-config.js

'gatsby-transformer-remark'

This plugin adds a few nodes to the GraphQL schema.

GraphQL with Gatsby

GraphQL is an alternative to REST for querying and updating data. Gatsby allows components to query for data using GraphQL. To explore GraphQL data, open localhost:8000/___graphql.

GraphQL query

The above image shows GraphiQL in action. The remark plugin makes allMarkdownRemark available in the GraphQL schema. With data available from markdown files, we can create new pages dynamically from the markdown files.

Creating page dynamically

There is a gatsby-node.js file which customises the creation of GraphQL nodes, pages, etc. When a new remark node is created, we will add a slug as an additional field to the node.

Open gatsby-node.js and export a new function.

exports.onCreateNode = ({ node, getNode, actions }) => {
    if (node.internal.type === 'MarkdownRemark') {
        const fileNode = getNode(node.parent);
        actions.createNodeField({
            node,
            name: 'slug',
            value: fileNode.name.toLowerCase()
        });
    }
}

Open GraphiQL and query for the slug and you should see the markdown filename as the slug.

{
  allMarkdownRemark {
    edges {
      node {
        fields {
          slug
        }
      }
    }
  }
}

Add another function to the same file to create pages dynamically.

exports.createPages = ({ graphql, actions }) => {
    graphql(`
        query {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `).then(result => {
        result.data.allMarkdownRemark.edges.forEach(edge => {
            actions.createPage({
                path: '/services/' + edge.node.fields.slug,
                component: path.resolve('./src/services/service.js'),
                context: {
                    slug: edge.node.fields.slug
                }
            });
        });
    });
}

This function makes a GraphQL query and for each markdown file, it adds a new page dynamically. The absolute path is /services/<slug>. And a new component in services folder renders that page. We pass the slug as a context to the service component.

Page to render markdown content

In the services folder, create a new file, service.js. This will be a page component. Page components can export a query variable which has a GraphQL query which will retrieve data for the component. For our service component, we need HTML from the markdown file.

export const query = graphql`
    query($slug: String!) {
        markdownRemark(fields: { slug: { eq: $slug } }) {
            html
            frontmatter {
                title
            }
        }
    }
`;

Import graphql from gatsby package.

import { graphql } from 'gatsby';

The data from GraphQL query is available in props.data object. To display the HTML, we destructure the props object and set the HTML within a div container.

export default function Service(props) {
    const {
        html,
        frontmatter: { title }
    } = props.data.markdownRemark;

    return (
        <Layout>
            <div>
                <h1>{title}</h1>
                <div dangerouslySetInnerHTML={{ __html: html }} />
            </div>
        </Layout>
    );
}

With these changes in place, we have the Service pages available. To navigate to the pages, open services.js and add links like so.

<ul className={styles.services}>
    <li>
        <Link to="/services/react-app">React apps</Link>
    </li>
    <li>
        <Link to="/services/react-native-app">React Native apps</Link>
    </li>
    <li>
        <Link to="/services/nextjs-app">Next apps</Link>
    </li>
    <li>
        <Link to="/services/gatsby-app">Gatsby apps</Link>
    </li>
</ul>

The Service page is shown below.

Service page

Build the app

Finally, it is time to build the app.

gatsby build

The build output is available in the /public folder. Run the built app using

gatsby serve

Open localhost:9000 to view the app.

The build folder can be deployed to any hosting service like Netlify, Github pages, Now, etc. When the page downloads, JavaScript also downloads. Subsequent navigation is all client-side on the browser.

Summary

We covered lot of ground in this tutorial. We learnt how to:

  • Style a page using CSS module
  • Add a new page
  • Navigate between pages
  • Customise Layout / Header
  • Read files from filesystem
  • Add a plugin
  • Query data using GraphQL
  • Add pages dynamically
  • Component to render dynamic pages
  • Build the app

The code for the tutorial is available in a Github repo. Please ask me any questions in the comments below.

Related Posts

2 thoughts on “Static website using Gatsby and Markdown

  1. sorry, but when I follow your steps, there are many errors in bulding site.
    you should update your article correctly

Leave a Reply

Your email address will not be published.