Migrate to Netlify Today

Netlify announces the next evolution of Gatsby Cloud. Learn more

ContactSign Up
Community Plugin
View plugin on GitHub

gatsby-source-google-docs-team

npm package

gatsby-source-google-docs-team is a Gatsby plugin to use Google Docs as a data source.

  • 🔥 No need for a CMS anymore.
  • 🖋 Write your blog posts on Google Docs.
  • 🗂 Organize your documents in one or multiple folder in Google Drive (trees allowed)
  • 🤡 Add custom metadata fields to yours documents
  • Supports Team drives
  • Optional Flag to download images to gatsby
  • Added folder name to node if a doc is inside a folder

It’s that simple

Getting started

Download gatsby-source-google-docs-team package

gatsby-source-google-docs

You can download gatsby-source-google-docs-team from the NPM registry via the npm or yarn commands

npm install gatsby-source-google-docs-team --save

Turn on the Google Docs API and set configuration

  • Follow the Step 1: Turn ON the Google Docs API
  • Turn ON the Google Drive API
  • Get a client_id and a client_secret from the Google console. If you downloaded credential.json file, you can extract them from it
  • Get an api_key from the Google console
  • Fill the gatsby-source-google-docs gatsby config object.

More info can be found on the official Google Docs quickstart guide.

Generate a token file

Run gatsby develop to generate a token file.

token_path can be customized in the configuration object (config/token_path).

Usage

Add the plugin to your configuration:

In your gatsby-node.js file, configure the gatsby-source-google-docs and the gatsby-transformer-remark plugins:

module.exports = {
    plugins: [
        {
            resolve: "gatsby-source-google-docs",
            options: {
                // Mandatory
                // --------
                foldersIds: ["FOLDER_ID_1", "FOLDER_ID_2"], // folders Ids can be found in Google Drive URLs
                config: {
                    api_key: "YOUR_API_KEY",
                    client_id: "YOUR_CLIENT_ID",
                    client_secret: "YOUR_CLIENT_SECRET",
                    // Optional
                    // --------
                    token_path: "google-docs-token.json",
                },
                // Optional
                // --------
                fields: ["createdTime"], // https://developers.google.com/drive/api/v3/reference/files#resource
                fieldsMapper: {createdTime: "date", name: "title"}, // To rename fields
                fieldsDefault: {draft: false}, // To add default fields values
                convertImgToNode: true, // To convert images to remote node files
            },
        },
        // Use gatsby-transformer-remark to modify the generated markdown
        // Not mandatary, but recommanded to be compliant with gatsby remark ecosystem
        {
            resolve: "gatsby-transformer-remark",
            options: {
                plugins: [],
            },
        },
    ],
}

Add an automatic slug generation

Modify your onCreateNode function in your gatsby-node.js to generate a slug field:

exports.onCreateNode = ({node, actions}) => {
    // You need to enable `gatsby-transformer-remark` to transform `GoogleDocs` type to `MarkdownRemark` type.
    if (node.internal.type === `MarkdownRemark`) {
        const customSlug = node.frontmatter.slug // If you add extra data `slug` with description field
        actions.createNodeField({
            name: `slug`,
            node,
            value: customSlug || node.frontmatter.path,
        })
    }
}

node.frontmatter.name contains the title of the Google Doc

Create a post template

Create a src/templates/post.js file where you will define your post template:

import React from "react"

const PostTemplate = ({data: {post}}) => (
    <>
        <h1>{post.frontmatter.name}</h1>
        <p>{post.frontmatter.date}</p>
        <div dangerouslySetInnerHTML={{__html: post.html}} />
    </>
)

export default PostTemplate

// You need to enable `gatsby-transformer-remark` to query `markdownRemark`.
// If you don't use it, query `googleDocs`
// If you use convertImgToNode then add googleDocImages query
export const pageQuery = graphql`
    query BlogPostBySlug($slug: String!) {
        post: markdownRemark(fields: {slug: {eq: $slug}}) {
            html
            frontmatter {
                name
                date(formatString: "DD MMMM YYYY", locale: "fr")
            }
        }
        googleDocImages: allFile(
            filter: {name: {glob: "google-doc-image-**"}}
        ) {
            edges {
                node {
                    id
                    name
                    childImageSharp {
                        fluid {
                            base64
                            tracedSVG
                            aspectRatio
                            src
                            srcSet
                            srcWebp
                            srcSetWebp
                            sizes
                            originalImg
                            originalName
                            presentationWidth
                            presentationHeight
                        }
                    }
                }
            }
        }
    }
`

Create a page for each post

Use the createPages API from gatsby in your gatsby-node.js to create a page for each post.

const path = require("path")

// You need to enable `gatsby-transformer-remark` to query `allMarkdownRemark`.
// If you don't use it, query `allGoogleDocs`
exports.createPages = async ({graphql, actions}) =>
    graphql(
        `
            {
                allMarkdownRemark(
                    sort: {fields: [frontmatter___date], order: DESC}
                ) {
                    edges {
                        node {
                            fields {
                                slug
                            }
                        }
                    }
                }
            }
        `
    ).then(result => {
        if (result.errors) {
            throw result.errors
        }
        result.data.allMarkdownRemark.edges.forEach((post, index) => {
            actions.createPage({
                path: post.node.fields.slug,
                component: path.resolve(`./src/templates/post.js`),
                context: {
                    slug: post.node.fields.slug,
                },
            })
        })
    })

Add extra data

If you need more data attached to your documents, fill the description field in Google Drive with a JSON object:

{
    "slug": "custom-url",
    "date": "2019-01-01",
    "author": "Yourself",
    "category": "yourCageory",
    "tags": ["tag1", "tag2"]
}

JSON will be transformed to YAML and added to your markdown frontmatter and ovveride the existing ones.

/!\ Do not use id, name, description or any Google Docs field you add to the config fields option

If convertImgToNode is enabled. You will need to search the id in the HTML file and replace it with Gatsby image tag

Create a post template

Create a src/templates/post.js file where you will define your post template:

import React from "react"
import Img from "gatsby-image"
import parse from "html-react-parser"

const PostTemplate = ({data: {post, googleDocImages}}) => {
    //This is needed if convertImgToNode is enabled
    const options = {
        replace: domNode => {
            const {children} = domNode
            if (!children) return
            const hasImgTag = children.find(img => img.name === "img")
            if (hasImgTag) {
                const {attribs} = hasImgTag
                const {src, alt} = attribs
                return (
                    <Img
                        fluid={
                            googleDocImages.edges.find(
                                ({node}) => node.id === src
                            ).node.childImageSharp.fluid
                        }
                        alt={alt}
                        className="ui fluid image"
                    />
                )
            }
        },
    }
    const htmlContent = parse(post.html, options)
    return (
        <>
            <h1>{post.frontmatter.name}</h1>
            <p>{post.frontmatter.date}</p>
            <React.Fragment>{htmlContent}</React.Fragment>
        </>
    )
}

export default PostTemplate

// You need to enable `gatsby-transformer-remark` to query `markdownRemark`.
// If you don't use it, query `googleDocs`
// If you use convertImgToNode then add googleDocImages query
export const pageQuery = graphql`
    query BlogPostBySlug($slug: String!) {
        post: markdownRemark(fields: {slug: {eq: $slug}}) {
            html
            frontmatter {
                name
                date(formatString: "DD MMMM YYYY", locale: "fr")
            }
        }
        googleDocImages: allFile(
            filter: {name: {glob: "google-doc-image-**"}}
        ) {
            edges {
                node {
                    id
                    name
                    childImageSharp {
                        fluid {
                            base64
                            tracedSVG
                            aspectRatio
                            src
                            srcSet
                            srcWebp
                            srcSetWebp
                            sizes
                            originalImg
                            originalName
                            presentationWidth
                            presentationHeight
                        }
                    }
                }
            }
        }
    }
`

Contributing

  • ⇄ Pull/Merge requests and ★ Stars are always welcome.
  • For bugs and feature requests, please create an issue.

See CONTRIBUTING guidelines

Changelog

See CHANGELOG

Special Thanks

gatsby-source-google-docs

License

This project is licensed under the MIT License - see the LICENCE file for details

© 2023 Gatsby, Inc.