Since i wrote this article Gatsby has added support for typescript out of the box, so many of this steps are not relevant anymore.
Intro
As a JAMstack & React enthusiast one of my favorite SSG - (Static Site Generator) Frameworks is Gatsbyjs.
I have used Gatsbyjs to create my company's website as well as a bunch of other customers' too.
A couple of years ago I started playing around with Typescript and instantly fell in love with it. It helped me write better and more predictable code, while it also provided a level of documentation around my codebase.
While Gatsbyjs supports Typescript out of the box, I found out that there was not enough documentation about their Config Files.
Inside its configuration files, Gatsby provides a rich set of lifecycle APIs to hook into its bootstrap, build, and client runtime operations. They allow sourcing data, creating pages, and customizing generated HTML pages.
In this article we will take a look on how to set up our Gatsby website and support it fully with Typescript.
Initial setup
After generating a Gatsby site with gatsby new
, you can immediately rename any file inside of /src
from .js
to .tsx
and it will work out of the box. So that's done. What you will find though is that files like gatsby-browser.js
, gatsby-node.js
or gatsby-ssr.js
wont work out of the box with this method.
Installing packages
Its really useful to install types for react, react-dom, node and probably react-helmet. You can install thouse via npm like so:
npm install @types/node @types/react @types/react-dom @types/react-helmet --save-dev
And we also need ts-node.
npm install ts-node
Then on gatsby-config.js
in the beginning of the file add:
// gatsby-config.js
require('ts-node').register({
compilerOptions: {
module: 'commonjs',
target: 'es2017',
},
})
module.exports = {
// rest of the config
...
}
tsconfig.json
Even though there is a plugin for it, I usually like to add my own tsconfig.json
file in the root of my project. My tsconfig.json
file looks like this:
{
"compilerOptions": {
"module": "commonjs",
"target": "esnext",
"jsx": "preserve",
"lib": [
"dom",
"es2015",
"es2017"
],
"strict": true,
"noEmit": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"resolveJsonModule": true
},
"include": [
"./src/**/*"
]
}
The rest of the files
After setting up my tsconfig.json
I create a folder inside /src
called app
. I usually store all my config and provider files there.
For the sake of time i will provide two example files that i use in every project and I am pretty sure you can figure out how to apply it to the rest.
gatsby-node.js - createPages
If you worked with Gatsby before this should look familiar with the only difference being that is written in Typescript.
Let's assume we want to create pages from a list of article we fed into Gatsby's internal graphql.
// src/app/GatsbyNode.tsx
import * as path from "path"
import { GatsbyNode } from "gatsby";
type TypePost = {
id: string
title: string
slug: string
content: string
}
type TypeData = {
allPost: {
nodes: TypePost[]
}
}
export const createPages: GatsbyNode["createPages"] = async ( { graphql, actions } ) => {
const { createPage } = actions
const data = await graphql<TypeData>( `
{
allPost {
nodes {
id
title
slug
content
}
}
}
` )
// Create Post Pages
const postTemplate = path.resolve("./src/templates/Post.tsx")
const createPostPromise = data?.allPost.nodes.map((post) => {
createPage({
path : `posts/${post.slug}`,
component : postTemplate,
context : {
slug: post.slug,
// anything else you want to pass to your context
}
})
})
await Promise.all( [ createPostPromise] )
}
Then on gatsby-node.js
we do this:
// gatsby-node.js
'use strict'
exports.onCreatePage = require("./src/app/GatsbyNode").onCreatePages
If you are using multiple apis inside of ./src/app/GatsbyNode.ts
like onCreatePage
or onCreateWebpackConfig
you can also do:
// gatsby-node.js
'use strict'
module.exports = require("./src/app/GatsbyNode")
This will run all the functions of ./src/app/GatsbyNode.ts
, but you have to make sure you export the functions with the correct name based on Gatsby's documentation.
gatsby-browser.js & gatsby-ssr.js - wrapRootElement
So let's assume we also want to add a theme provider to our app. On the src/app/
folder that we created before we add the file WrapRootElement.tsx
// src/app/WrapRootElement.tsx
import React from "react";
import { GatsbyBrowser } from "gatsby";
import { ThemeProvider } from "./ThemeContext";
const wrapRootElement: GatsbyBrowser['wrapRootElement'] = ( { element } ) => {
return (
<ThemeProvider>
{ element }
</ThemeProvider>
)
}
export default wrapRootElement
Then on gatsby-browser.js
& gatsby-ssr.js
files:
// gatsby-browser.js & gatsby-ssr.js
import WrapRootElement from "./src/app/WrapRootElement";
export const wrapRootElement = WrapRootElement
Conclusion
I hope this tutorial helps you use Gatsby & Typescript more effectively and save you some time searching how to actually use the config files of Gatsby with Typescript. Thank you for your time!!!
Top comments (2)
Thanks
Thanks for writing this, as you say Gatsby are a little light on info for setting up a project so it's very helpful. I just had to add typescript as a dev dependency as well by the way.