This article has been translated on the basis of machine translation. If there are any mistakes, please fix it.pull request

Gatsby config with TypeScript

Explains how to make a config file with TypeScript in Gatsby. It mentions the different execution environments and module systems for each Config file, and explains the peculiarities of gatsby-ssr.

4/1/20225 min read
..
hero image

Introduction

There are many articles on Gatsby that deal with components and graphql with TypeScript. However, I have rarely seen any mention of Gatsby config.

Type-safety of configuration files is just as important as components. In this article, I will focus on Gatsby config and explain how to use with TypeScript.

Gatsby config's module system

First, I briefly summarized the module system of Gatsby config.

FileModule systemTypes of export
gatsby-browserES Modules | CommonJSNamed Exports
gatsby-configCommonJSDefault Exports
gatsby-nodeCommonJSNamed Exports
gatsby-ssrES Modules | CommonJSIf the API has been exported, it will be loaded. Named Exports

Only ES Modules and CommonJS are mentioned.

As for features, gatsby-config and gatsby-node assume CommonJS. On the other hand, gatsby-browser and gatsby-ssr can use both ES Modules and CommonJS. Also, If there are multiple APIs, they need to be Named Exports1.

In addition, gatsby-ssr behaves a bit differently. In order for gatsby-ssr to run, the Gatsby SSR APIs must have a Named Exports.

If there is no Named Exports, it will not be executed.

1
console.log('not exec')
gatsby-ssr.jsjs

Example to be executed:

1
2
3
4
5
6
7
8
9
console.log('exec')
const onRenderBody = () => {
console.log('onRenderBody')
}
export {
onRenderBody
}
gatsby-ssr.jsjs
1
2
exec
onRenderBody
bash

Also, the Default Exports does not mean that you have exported.

1
2
3
4
5
const onRenderBody = () => {
console.log('not exec')
}
export default { onRenderBody }
gatsby-ssr.jsjs

Additionally, in common with all configs, extensions such as .ts and .tsx are not loaded by default.

With these situations in mind, let's make Gatsby's configuration files fully TypeScript so that they can be run in a type-safe manner.

The order in which Gatsby config is read

Gatsby config will be loaded in the following order.

PlantUML

The gatsby-config will be loaded first in the configuration file.

So let's take a look at gatsby-config.

gatsby-config with TypeScript

First, install esbuild-register to transpile TypeScript.

1
yarn add -D esbuild-register
bash

Since gatsby-config must be a .js file in CommonJS format, we will leave gatsby-config.js as is. Then, create a new gatsby-config.ts file anywhere you want. In this case, I created it under the root directory.

1
2
3
4
5
6
.
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-config.ts
├── gatsby-node.js
└── gatsby-ssr.js
bash

From now on, we will configure gatsby-config.ts with types. It will look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import type { GatsbyConfig } from 'gatsby'
import { resolve } from 'path'
const plugins: GatsbyConfig['plugins'] = [
'gatsby-plugin-image',
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'posts',
path: resolve(__dirname, 'posts')
}
},
...
]
const siteMetadata: GatsbyConfig['siteMetadata'] = {
siteUrl: 'https://miyauchi.dev/',
}
const config: GatsbyConfig = {
siteMetadata,
plugins
}
export default config
gatsby-config.tsts

A type GatsbyConfig is provided, which we will use for type annotations. You can split the variables as you see fit, since you can just export the defaults in the end. If the plugin becomes too bloated, you can split the file.

When specifying a path, use __dirname to specify the absolute path to avoid mistakes.

You can load this file from gatsby-config.js.

Transpile at runtime with esbuild-register

TypeScript can be transpiled at runtime with esbuild-register.

Change gatsby-config.js as follows:

1
2
3
4
5
6
7
const { register } = require('esbuild-register/dist/node')
register({
target: 'node16'
})
module.exports = require('./gatsby-config.ts')
gatsby-config.jsjs

We are doing two things here.

  • esbuild-register to transpile TypeScript with esbuild.
  • gatsby-config.js to re-export gatsby-config.ts.

esbuild-register is faster than ts-node because it has no type checking.

This completes gatsby-config.

Also, since esbuild-register is executed in gatsby-config, which is the first file loaded TypeScript in other config files will be automatically transpiled by esbuild.

So, for files other than gatsby-config, you only need to set them to .ts and add type annotations.

gatsby-node:

1
2
3
4
5
6
7
8
9
10
11
import type { GatsbyNode } from 'gatsby'
import { resolve } from 'path'
const createPages: GatsbyNode['createPages'] = async ({
graphql,
actions,
reporter
}) => {
// ...
}
export { createPages }
gatsby-node.tsts

Of course, tsx can also be available.

gatsby-ssr:

1
2
3
4
5
6
7
8
9
10
import React from 'react'
import type { GatsbySSR } from 'gatsby'
const wrapPageElement: GatsbySSR['wrapPageElement'] = ({
element,
}) => {
return <div className='wrap'>{element}</div>
}
export { wrapPageElement }
gatsby-ssr.tsxtsx

gatsby-browser:

1
2
3
4
5
6
7
8
9
10
import React from 'react'
import type { GatsbyBrowser } from 'gatsby'
const wrapPageElement: GatsbyBrowser['wrapPageElement'] = ({
element,
}) => {
return <div className='wrap'>{element}</div>
}
export { wrapPageElement }
gatsby-browser.tsxtsx

Note that you basically have to do a Named Exports. Gatsby provides the basic types, so I just need to annotate the types.

Summary

The end result is the following file structure:

1
2
3
4
5
6
.
├── gatsby-browser.tsx
├── gatsby-config.js
├── gatsby-config.ts
├── gatsby-node.ts
└── gatsby-ssr.txs
bash

We use gatsby-config.js as the entry point because gatsby-config is loaded first. Registering esbuild-register in gatsby-config.js will cause TypeScript to be transpiled in subsequent files.

Unfortunately, we don't know how to get rid of gatsby-config.js. To get rid of it, you have to register esbuild-register during the gatsby command.

For example, in node.js, there is a command argument -r, and you can pass esbuild-register as an argument.

1
node -r esbuild-register a.ts
bash

This will allow you to transpile and execute the TypeScript file. Similarly, if you can register esbuild-register when running the gatsby command, you may be able to eliminate

If you know anything about this, please comment.

Anyway, now you can run the type safely.


  1. This section mentions how to export in ES6 with TypeScript.

Edit this page on GitHub

Other Article

Comments