How to use Gatsby with Typescript.
Tags: react, typescript, gatsby, tutorial
Published: 2021-09-17 20:19:23

_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](https://www.gatsbyjs.org/).

I have used [Gatsbyjs](https://www.gatsbyjs.org/) to create my [company's website](http://emmbrand.com/) 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](https://www.gatsbyjs.com/docs/reference/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:

```bash
npm install @types/node @types/react @types/react-dom @types/react-helmet --save-dev
```

And we also need ts-node.

```bash
npm install ts-node
```

Then on `gatsby-config.js` in the beginning of the file add:

```js
// 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:

```json
{
  "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.

```ts
// 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:

```js
// 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:

```js
// 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`

```ts
// 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:

```js
// 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!!!