Setup a modern Jamstack project using Gatsby, TypeScript, Styled Components, and Contentful
Tags: gatsby, typescript, jamstack, react
Published: 2022-02-26 21:37:59

In this post I will walk you through setting up a modern Jamstack project using Gatsby, TypeScript, Styled Components, and Contentful! Contentful and Gatsby work very well together, as Contentful allows you to focus on easily creating content for your site, and Gatsby provides a super fast, static site. 

Here's a quick rundown of the tech we'll be using:

- [Gatsby](https://www.gatsbyjs.com/)
- [Styled Components](https://styled-components.com/)
- [TypeScript](https://www.typescriptlang.org/)
- [Contentful](https://www.contentful.com/)
- [Gatsby Cloud](https://www.gatsbyjs.com/products/cloud/)

Before we started, there are a few prerequisites:

- [Node.js](https://nodejs.org/en/) (which comes with NPM) is installed on your machine
- Text editor of your choice (I will be using [VS Code](https://code.visualstudio.com/))

Something else I will also mention is that I use Linux, so all the commands listed below work in a UNIX environment, such as Mac or Linux. For Windows, some of these commands may not work, so you will have to find out the equivalent.

Please check out the [repo](https://github.com/andrews1022/contentful-blog-gatsby-starter) I've created with the finished files. You can use this if you get stuck and need to reference anything. 

You'll also notice that I make push commits to GitHub frequently throughout the process. I like this approach because it's easier for me to see incremental progress rather than a large number of changes all at once.

Lastly, I prefer to stick with NPM. If you use Yarn, simply replace NPM commands (such as `npm i`) with the corresponding Yarn command (`yarn add`).

With all that out of the way, let's get started!

## Contentful Setup Pt. 1

The first thing we'll do is set up a free account with Contentful. You can do that [here](https://www.contentful.com/sign-up/). To keep things simple, I would recommend signing up with your GitHub account.

After you've created your account, you should see your empty space (or be prompted to create one, and please do if you are). It is important that you do **NOT** add anything to this space. The reason why will come up soon!

Go to `Settings`, and then `API Keys`. Make sure you are on the `Content delivery / preview token` tab. Click `Add API key` in the top right corner, and then give the keys a name, something like "Gatsby Blog" for example, and perhaps also a short description, then click `Save`.

Make sure to keep this tab open as we will definitely need it later!

## GitHub Setup

First, create a new GitHub repo [here](https://github.com/new). Give it at least a name, and perhaps also a short description, then click `Create repository`. Keep this tab open, we'll need it in just a bit!

On your local machine, open up your terminal of choice, and `cd` where you store your projects. From there, create a new directory and then go into it: 

`mkdir PROJECT_NAME && cd PROJECT_NAME`

`PROJECT_NAME` here being the name of the repo. 

Next, download the [Gatsby Contentful starter](https://github.com/contentful/starter-gatsby-blog):

`npx gatsby new . https://github.com/contentful/starter-gatsby-blog`

Using `npx` means we don't have to install the `gatsby` package globally on our machine, which I personally prefer. 

After the starter is done downloading, open it in your code editor of choice. Again, I use VS Code, so I can run the command `code .` and it will open the project in VS Code for me.

Next, let's remove `git` from this folder so we can start from scratch: 

`rm -rf .git`

Finally, go back to the GitHub tab in your browser and run each of the git commands listed. If you want to make things easier on yourself, here they all are below in one long command:

```
git init && git add . && git commit -m "project setup" && git branch -M main && git remote add origin https://github.com/GITHUB_USERNAME/PROJECT_NAME.git && git push -u origin main
```

Just make sure to replace `GITHUB_USERNAME` with your GitHub username, and `PROJECT_NAME` with the name of the repo you just created.

## Contentful Setup Pt. 2

Now, typically when you finish downloading a React boilerplate/starter project such as this, you may be inclined to start up the local development server and take a look. Well, you can do that here too, but as you may have guessed by the way I said that first thing, it's not going to work. If you run the command `npm run dev` to start up the local dev server, you'll see an error like this:

`Error: Contentful spaceId and the access token need to be provided.`

At this point, I want to give props (pun _absolutely_ intended) to the Contentful team because with this starter, they have actually included a setup script for us! This script will generate a couple of basic content models in our space, as well as a few pieces of starting content! This is why it's important to keep the space empty, so that the setup script can populate it. It's as simple as running the command: `npm run setup`.

Once you run this command, you will have to enter your API keys in the following order:

- Space ID
- Content Management API Access Token *
- Content Delivery API Access Token

Go back to your browser and go to the tab/window you had open with Contentful. You can easily copy and paste in your Space ID first, but, wait...where's the Content Management API Access Token? And why is there a * next to it above?

For this, I would recommend clicking on Settings, then clicking on API keys but this time open it in a new tab. Here, you can click on the `Content management tokens` tab. Click `Generate personal token`, give the token a name, and then click `Generate`. Copy and paste this token into the terminal. Then go back to the other tab and copy and paste in your `Content Delivery API Access Token`.

The reason we did it this way is because if you:

- Got your Space ID
- Went back, got your Content Management API Access Token
- Went back again, got your Content Delivery API Access Token

It's just a lot of back and forth in the same tab. 

Also, as you would have seen when you generate your `Content Management API Access Token`, this token will **NO LONGER** be accessible once you close the tab / move away from this page. Save it if you wish, but we actually do not need it at any other point in this process. We just needed it for the setup script.

After that is done, now you can run `npm run dev` to start the local development server!

## Gatsby Cloud Setup

For deployment, we'll be using [Gatsby Cloud](https://www.gatsbyjs.com/products/cloud/). Gatsby Cloud is set up to optimize your Gatsby site, and adding a new site is very easy to do.

First, you'll need to create a free account if you do not have one already. You can sign up [here](https://www.gatsbyjs.com/products/cloud/).

For any subsequent visits, you can go straight to your dashboard [here](https://www.gatsbyjs.com/dashboard/).

Once you are in your dashboard, click `Add a site +`. Choose to import a GitHub repository (at this point you will have to authorize Gatsby Cloud to access your GitHub repos if this is your first time using it). Find the repo you created, and click `Import`.

For `Basic Configuration`, you can leave the settings as-is and click `Next`.

For `Connect Integrations`, Gatsby Cloud should automatically detect you're using Contentful based on your `gatsby-config`. Click `Connect`, then click `Authorize`, then click `Authorize` again. Select the Space you created earlier, then click `Continue`.

For Environment variables, Gatsby Cloud actually sets up a couple extra ones for us that we don't need to use. You only need the following:

- Build Variables
  - `CONTENTFUL_ACCESS_TOKEN` --> Your `Content Delivery API access token`
  - `CONTENTFUL_SPACE_ID` --> Your `Space ID`
- Preview Variables
  - `CONTENTFUL_PREVIEW_ACCESS_TOKEN` --> Your `Content Preview API access token`
  - `CONTENTFUL_HOST` --> `preview.contentful.com`
  - `CONTENTFUL_SPACE_ID` --> Your `Space ID`

If you're wondering how I figured that out, I found [this piece of documentation](https://support.gatsbyjs.com/hc/en-us/articles/360052076554-Connecting-to-Contentful-Manually) which outlines what you need.

After you've filled in all the variables, click `Save`. Then click `Build site`. The build can take a couple of minutes, so you will have to wait! But, it should build successfully and now our site is deployed to Gatsby Cloud for all the world to see!

## Testing the Workflow

Before we continue, let's take just a moment to test / make sure our workflow can do 2 things. Whenever we either

- Push code to GitHub
- Make a change in Contentful

Gatsby Cloud should automatically rebuild the site. But, we haven't setup any webhooks? How will Gatsby Cloud know when to rebuild? 

Wrong! That was actually done automatically for us when we added the site to Gatsby cloud. In fact, if you go to your Contentful space, then go `Settings`, and then `Webhooks`, you should see one there!

If you do not, no worries! The documentation I linked above also includes the steps for configuring webhooks. So, just follow the steps and you'll be good to go.

### Simple Code Change

In VS Code, go to `/src/components/article-preview.js`. Find this piece of JSX: 

```javascript
<h2 className={styles.title}>{post.title}</h2>
```

We'll make a very simple change, such as adding on a few exclamation points: 

```javascript
<h2 className={styles.title}>{post.title}!!</h2>
```

Next, commit / push the change: 

`git add . && git commit -m 'quick commit for testing workflow' && git push -u origin main`

Go to your Gatsby Dashboard. This should've triggered a rebuild of the site (you might need to just refresh the page so that it is).

### Simple Contentful Change

As mentioned previously, the setup script we ran earlier created some starter content models and content for us, so we will make a simple change to the Person content `John Doe`.

Go to your Contentful Space, then go to the Content tab, and click on the `John Doe` piece of content. Make a simple change, such as changing the name to your name, then click `Publish Changes`.

Go to your Gatsby Dashboard. This should've triggered a rebuild of the site (you might need to just refresh the page so that it is).

The build time for this (at least in my experience) is typically VERY quick, only 3 - 5 seconds! Although, if you are changing/adding in a LOT of content, it will likely take longer.

So, at this point, we have confirmed whenever we either:

- Commit / push code to GitHub
- Make a change in Contentful

Gatsby Cloud will automatically trigger a re-build of the site, keeping it up-to-date at all times!

## Starter Cleanup

As is typically the case with starters/boilerplates, there are some things we don't need to keep around.

### Removing Unnecessary Files & Folders

First, let's remove some of the files & folders at the root level of the project. After some testing, here is a list of the files folders we can & cannot delete post-setup:

✓ --> CAN be removed
✕ --> CANNOT be removed

[✓] `.cache` --> Can be deleted, but is regenerated each time you rebuild, and is ignored by git anyways
[✓] `/bin` & related `package.json` scripts --> Used for running `npm run dev` to setup Contentful
[✓] `/contentful` --> Used for running `npm run dev` to setup Contentful
[✓] `/node_modules` --> Can be deleted, but is regenerated each time you install packages, and is ignored by git anyways
[✓] `/public` --> Can be deleted, but is regenerated each time you rebuild, and is ignored by git anyways
[✕] `/src` --> Essential
[✕] `/static` --> Used to house files like `robots.txt` and favicon
[✓] `_config.yml` --> Used for GitHub pages and we're using Gatsby Cloud
[✕] `.babelrc` --> Babel config file
[✓] `.contentful.json.sample` --> Sample Contentful data file
[✕] `.gitignore` --> Used to intentionally ignore/not track specific files/folders
[✕] `.npmrc` --> configuration file for NPM, defines the settings on how NPM should behave when running commands
[✕] `.nvmrc` --> specify which Node version the project should use
[✓] `.prettierrc` --> Config for Prettier. This is entirely subjective, so it is up to you if you wish to delete it or not. I use Prettier settings in VS Code
[✓] `.travis.yml` --> Config file for Travis CI. Travis CI is a hosted continuous integration service
[✓] `app.json` --> Unsure what this is used for, as it is not used anywhere in the project
[✕] `gatsby-config.js` --> Essential
[✕] `gatsby-node.js` --> Essential
[✕] `LICENSE` --> Okay to leave
[✓] `package-lock.json` --> can be deleted, but is regenerated each time you install packages
[✕] `package.json` --> Essential
[✕] `README.md` --> Essential
[✓] `screenshot.png` --> Was used in the README, but is no longer needed
[✓] `static.json` --> Unsure what this is used for, as it is not used anywhere in the project. Possibly used for Heroku
[✓] `WHATS-NEXT.md` --> Simple markdown file

You can use this command to remove all the files with a ✓ next to them at once:

```
rm -rf bin contentful _config.yml .contentful.json.sample .prettierrc .travis.yml app.json package-lock.json screenshot.png static.json WHATS-NEXT.md
```

Let's commit this progress:

`git add . && git commit -m 'removed unnecessary files and folders' && git push -u origin main`

### Updating NPM Scripts

Next, we'll quickly update our scripts in `package.json`.

First, let's add the `gatsby clean` script back in (I've found most starters remove it):

`"clean": "gatsby clean"`

Next, update the dev command to be: 

`"dev": "npm run clean && gatsby develop"`

This is really handy as it'll delete the `.cache` and `public` folders each time we start up the development server, which gives us the latest changes from Contentful. If you don't want this, you can simply add on another script:

`"start": "gatsby develop"`

But this is not necessary, and you'll see why later. 

I've also found this utility script I created for myself a while ago has really come in handy:

`"troubleshoot": "rm -rf .cache node_modules public package-lock.json && npm i && npm run dev"`

This is basically a hard reset of the project.

Let's commit this progress:

`git add . && git commit -m 'updated package.json scripts' && git push -u origin main`

At this point, I personally encountered a git error along the lines of:

`Fatal unable to access, could not resolve host when trying to commit changes.`

If this happens, it's likely a proxy issue. Simply run this command and it should fix the problem: 

`git config --global --unset http.proxy && git config --global --unset https.proxy`

### Components & Pages

Somewhat frustratingly, the starter uses a mix of Classes and Functions for components & pages. Let's convert all the files using classes to use the function syntax. Specifically, the function expression syntax. This makes it easier when we convert the files to TypeScript later when everything is consistent.

The files we need to adjust are:

- `src/components/layout.js`
- `src/pages/blog.js`
- `src/pages/index.js`
- `src/templates/blog-post.js`

Furthermore, all the component files use kebab-case for naming. Personally, I prefer to use PascalCase, as I am used to in other React projects. So, I will update all file names to use PascalCase instead. I understand that they are likely all kebab-case to be consistent with the naming of the pages and templates, so this just a personal preference.

As a quick reminder, when working with Gatsby, it is very important that you **DO NOT** rename page files to use PascalCase. Gatsby uses the file name for routing, so if you change `blog.js` to `Blog.js`, the route will no longer be `/blog`, but `/Blog`.

Lastly, I will group each component and it's CSS module file together in a folder to keep things organized. The file/folder structure will now be:

```
/components
  /ArticlePreview
    - index.js
    - article-preview.module.css
  /Container
    - index.js
  /Footer
    - index.js
    - footer.module.css
  etc.
```

Again, this is just my personal approach that I've always used. Totally up to you how you want to organize things.

Later on when we set up Styled Components, we will replace each `module.css` file with a `styles.ts` file. This `styles.ts` file will house any styled components used _only_ by the functional component in the same folder. So, the structure then will be:

```
/components
  /ArticlePreview
    - index.tsx
    - styles.ts
  /Container
    - index.tsx
  /Footer
    - index.tsx
    - styles.ts
  etc.
```

So, I will not bother renaming the CSS module files since they will be replaced anyways.

If you wish to convert these on your own, by all means please do! Below I've provided the code you will need. You can check out the repo which I linked to earlier again [here](https://github.com/andrews1022/contentful-blog-gatsby-starter/tree/main/src) if you wish, but keep in mind since they're all in TypeScript and we have converted them over yet.

layout.js:
```javascript
const Layout = ({ children, location }) => {
  return (
    <>
      <Seo />
      <Navigation />
      <main>{children}</main>
      <Footer />
    </>
  );
};

export default Layout;
```

blog.js:
```javascript
const BlogIndex = ({ data, location }) => {
  const posts = data.allContentfulBlogPost.nodes;

  return (
    <Layout location={location}>
    <Seo title='Blog' />
    <Hero title='Blog' />
    <ArticlePreview posts={posts} />
    </Layout>
  );
};

export default BlogIndex;
```

With Gatsby, pages access the data returned from the GraphQL query via `props.data`. We can tidy up the code a bit by destructuring our props in the ( ). We will use this approach for the remaining files.

index.js:
```javascript
const Home = ({ data, location }) => {
  const posts = data.allContentfulBlogPost.nodes;
  const [author] = data.allContentfulPerson.nodes;

  return (
    <Layout location={location}>
      <Hero
        image={author.heroImage.gatsbyImageData}
        title={author.name}
        content={author.shortBio.shortBio}
      />
      <ArticlePreview posts={posts} />
    </Layout>
  );
};

export default Home;
```

blog-post.js:
```javascript
const BlogPostTemplate = ({ data, location }) => {
  const post = data.contentfulBlogPost;
  const previous = data.previous;
  const next = data.next;

  return (
    <Layout location={location}>
    <Seo
      title={post.title}
      description={post.description.childMarkdownRemark.excerpt}
      image={`http:${post.heroImage.resize.src}`}
    />
    <Hero
      image={post.heroImage?.gatsbyImageData}
      title={post.title}
      content={post.description?.childMarkdownRemark?.excerpt}
    />
    <div className={styles.container}>
      <span className={styles.meta}>
      {post.author?.name} &middot; <time dateTime={post.rawDate}>{post.publishDate}</time> –{' '}					 
      {post.body?.childMarkdownRemark?.timeToRead} minute read
      </span>

      <div className={styles.article}>
        <div className={styles.body} dangerouslySetInnerHTML={{ __html: post.body?.childMarkdownRemark?.html }} />

        <Tags tags={post.tags} />

        {(previous || next) && (
          <nav>
            <ul className={styles.articleNavigation}>
              {previous && (
                <li>
                  <Link to={`/blog/${previous.slug}`} rel='prev'>											 
                    ← {previous.title}								 
                  </Link>
                </li>
              )}
              {next && (									 
                <li>
                  <Link to={`/blog/${next.slug}`} rel='next'>
                    {next.title} →
                  </Link>
                </li>
              )}
            </ul>
          </nav>
        )}
       </div>
      </div>
    </Layout>
  );
};
```

Let's commit this progress:

`git add . && git commit -m 'updated components and pages to use function syntax' && git push -u origin main`

### Uninstalling Some NPM Packages

At this point, we are no longer using the following packages:

- `contentful-import`
- `gh-pages`
- `lodash`
- `netlify-cli`

We can uninstall them all by running: 

`npm un contentful-import gh-pages lodash netlify-cli`

We can also simplify our `scripts` in `package.json` to:

```javascript
"scripts": {
  "build": "gatsby build",
  "clean": "gatsby clean",
  "dev": "gatsby develop",
  "rebuild": "rm -rf .cache public && npm run dev",
  "serve": "gatsby serve",
  "troubleshoot": "rm -rf .cache node_modules public package-lock.json && npm i && npm run dev"
}
```

Let's commit this progress:

`git add . && git commit -m 'uninstalled some npm packages and updated package.json scripts' && git push -u origin main`

### Organizing Components Into Folders

First, go into the components folder: `cd src/components/`

We need to create all the necessary folders for each component:

- ArticlePreview
- Container
- Footer
- Hero
- Layout
- Navigation
- Seo
- Tags

We can create all these folders at once by running the command: 

`mkdir ArticlePreview Container Footer Hero Layout Navigation Seo Tags`

Now, one at a time, move the corresponding files into their folders. Hopefully VS Code automatically updates the import path(s) for you. If not, you will have to manually update them yourself.

After moving everything around, you should see the following warning:

`warn chunk commons [mini-css-extract-plugin]`

This error/warning is caused by the Webpack plugin `mini-css-extract-plugin` wanting all CSS imports to be in the same order. This is because it has confused CSS modules with plain CSS. However, since we will be using Styled Components, we can ignore this warning and continue.

Let's commit this progress:

`git add . && git commit -m 'organized components into folders' && git push -u origin main`

## Converting to TypeScript

**_UPDATE_**: As of [Gatsby v4.8](https://www.gatsbyjs.com/docs/reference/release-notes/v4.8/), there is full TypeScript for the `gatsby-browser` and `gatsby-ssr files`. Also, as of [Gatsby v4.9](https://www.gatsbyjs.com/docs/reference/release-notes/v4.9/), there is full TypeScript for the `gatsby-config` and `gatsby-node` files! So, if you are able to use those versions, check out the 2 links on how to best setup those files!

Now comes a BIG step: converting everything to TypeScript! We will convert all components, pages, and even the Gatsby API files (gatsby-config, gatsby-node, etc.) at the root level to use TypeScript.

For this portion, I want to give a huge thanks to _Progressive Dev_ on YouTube. His [video](https://www.youtube.com/watch?v=Mf8eqNrcZDw) was immensely helpful when I first wanted to work with Gatsby and TypeScript.

Gatsby claims to support TypeScript out of the box.and this is partially true. If we create a simple `Copy` component `Copy.tsx`:

```javascript
const Copy = () => (
  <p>Lorem ipsum dolor sit amet consectetur.</p>
);
```

And use it in `ArticlePreview` above the tags, for example, it will work just fine. However, we don't get 100% proper type checking. VS Code will highlight the error, but the Gatsby CLI will not.

The other rather annoying thing to do is we have to manually convert all the `.js`/`.jsx` files to `.ts`/`.tsx` files as Gatsby does not have TypeScript versions of their starters.

Here is a summary of the steps we will take:

- Setup `tsconfig.json`
- Convert all the components & pages to TypeScript
- Convert the Gatsby API files to use TypeScript

### Setup

To start things off, let's install the TypeScript package:

`npm i typescript`

Also install the following @types packages: 

`npm i @types/node @types/react @types/react-dom @types/react-helmet`

Next, create a tsconfig file: 

`tsc --init`

Select everything in `tsconfig.json`, and replace it with this:

```javascript
{
  "compilerOptions": {
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "jsx": "react",
    "module": "commonjs",
    "noEmit": true,
    "pretty": true,
    "skipLibCheck": true,
    "strict": true,
    "target": "es5"
  },
  "include": ["./src", "gatsby"],
  "exclude": ["./node_modules", "./public", "./.cache"]
}
```

You can always add on more, but this will suffice for now.

Next, we need a way to run our local development server, AND get proper type checking in the CLI. For this, we can use the package `concurrently`: 

`npm i concurrently`

`concurrently` will allow us to run multiple scripts, well, concurrently! Next, let's update our `scripts` in `package.json` to use `concurrently`:

```javascript
"dev-gatsby": "gatsby develop",
"dev-typescript": "tsc -w",
"dev": "npm run clean && concurrently \"npm:dev-gatsby\" \"npm:dev-typescript\""
```

At this point, we should run `npm run dev` to start up the local dev server and make sure everything is still working fine.

### Converting Pages

Now, we can convert the `.js` files to `.tsx` files. Let's start with the Home Page. I will walk you through the process once, and leave it up to you to repeat the process for the other pages/templates. 

To start, rename the file from `index.js` to `index.tsx`. When you do that though, TypeScript will complain about a few things:

- It's not sure what the types of the components are that we are using. That is because they are still plain `.js` files, and we will convert them to `.tsx` in a bit anyways, so no worries there
- The props `data` & `location` have the any type implicitly
- The types for `allContentfulBlogPost` & `allContentfulPerson` are also unknown
- A type error for the CSS Modules. Again, since we are replacing them with Styled Components later on, no worries here either

Luckily, Gatsby has types for us, and the one we need to use for pages is `PageProps`. So, import it:

```javascript
import type { PageProps } from 'gatsby'
```

You'll notice here that I specifically put `import type` at the beginning. I do this because while this:

```javascript
import { PageProps } from 'gatsby'
```

Is perfectly fine and will work without issue, I think it's a bit misleading. When I see that, my initial reaction is that `PageProps` is a component. But it is not, it is a type. This syntax for importing types also works:

```javascript
import { type PageProps } from 'gatsby'
```

But I prefer the way I initially did it because if we import multiple types like this, for example:

```javascript
import { type PageProps, type AnotherType, type YetAnotherType } from 'gatsby'
```

It looks kind of messy. We can simplify it by having the single `type` in front of the curly braces. Also, using `{ type PageProps }` is a newer syntax, and might not work with older versions of React (say an old create-react-app you have, or something like that). 

So, the syntax `type { PageProps }` then is the better choice because:

- We only use the `type` keyword once, making the code a bit cleaner
- It can be used with older and current React + TypeScript projects

Alright, back to the page! We can then set the type of our destructured props to PageProps:

```javascript
const Home = ({ data, location }: PageProps) => {
  // ...
};
```

Next, outside the function body, just above, create a new type called `GraphQLResult`: 

```javascript
type GraphQLResult = {};

const Home = ({ data, location }: PageProps) => {
  // ...
};
```

Inside this object, we need to set the types for the GraphQL data being returned. 

Now would be a good time to create a `types` folder, and inside it a file called `types.ts`. 

`types.ts` will house our re-usable types throughout the project. I typically use just the one file, but you can certainly separate types for specific things into their own files if you wish. For example:

- `/types/global.ts`
- `/types/graphql.ts`

At the top, import the following: 

```javascript
import type { IGatsbyImageData } from 'gatsby-plugin-image';
```

We will use this type multiple times in this file alone, and I know from experience that we would get type errors where we use the `GatsbyImage` component if we didn't.

In `types.ts`, add the following:

```javascript
export type BlogPost = {
  title: string;
  slug: string;
  publishDate: string;
  tags: string[];
  heroImage: {
    gatsbyImageData: IGatsbyImageData;
  };
  description: {
    childMarkdownRemark: {
      html: string;
    };
  };
};

export type Person = {
  name: string;
  shortBio: {
    shortBio: string;
  };
  title: string;
  heroImage: {
    gatsbyImageData: IGatsbyImageData;
  };
};
```

Back in `index.tsx`, adjust the `GraphQLResult` type we created to:

```javascript
type GraphQLResult = {
  allContentfulBlogPost: {
    nodes: BlogPost[];
  };
  allContentfulPerson: {
    nodes: Person[];
  };
};
```

Make sure to import these types too, of course. Now, we can pass this type in as an additional argument to PageProps:

```javascript
const Home = ({ data, location }: PageProps<GraphQLResult>) => {
  // ...
};
```

And now the type errors for the Contentful data should be gone!

You should be able to repeat this process for `blog.js` without issue. `blog.js`, or rather `blog.tsx`, will use the `BlogPost` type as well. 

If you are stuck, you can always take a look at the final code [here](https://github.com/andrews1022/contentful-blog-gatsby-starter/blob/main/src/pages/blog.tsx).

For converting blog-post.js to blog-post.tsx, there are a couple of extra steps. After renaming it to `.tsx`, you will get an error saying `Module not found`. 

This is because in `gatsby-node.js`, there is this line:

```javascript
const blogPost = path.resolve('./src/templates/blog-post.js');
```

Simply change it to `.tsx` at the end there. Then, in `types.ts`, add the following:

```javascript
export type SingleBlogPost = {
  author: {
    name: string;
  };
  body: {
    childMarkdownRemark: {
      html: string;
      timeToRead: number;
    };
  };
  description: {
    childMarkdownRemark: {
      excerpt: string;
    };
  };
  heroImage: {
    gatsbyImageData: IGatsbyImageData;
    resize: {
      src: string;
    };
  };
  publishDate: string;
  rawDate: string;
  slug: string;
  tags: string[];
  title: string;
};

export type NextPrevious = { slug: string; title: string } | null;
```

Back in the `blog-post.tsx`, adjust the `GraphQLResult` type to:

```javascript
type GraphQLResult = {
  contentfulBlogPost: SingleBlogPost;
  next: NextPrevious;
  previous: NextPrevious;
};
```

Then pass it to PageProps like before:

```javascript
const BlogPostTemplate = ({ data, location }: PageProps<GraphQLResult>) => {
  // ...
};
```

And with that, all our pages are now using TypeScript! Let's commit this progress:

`git add . && git commit -m 'updated pages to use typescript' && git push -u origin main`

### Converting Components

Now let's update the components to `.tsx`! The steps for this process are much simpler than with converting pages:

- Rename `.js` to `.tsx`
- Setup type for the props (if any)

For example, `ArticlePreview`:

```javascript
// props
type ArticlePreviewProps = {
  posts: BlogPost[];
};

const ArticlePreview = ({ posts }: ArticlePreviewProps) => {
  // ...
};
```

Again, if you are having trouble/unsure how to type the pre-existing components, you can see how I did so [here](https://github.com/andrews1022/gatsby-contentful-blog/tree/main/src/components).

After converting all components to TypeScript, let's commit this progress:

`git add . && git commit -m 'updated components to use typescript' && git push -u origin main`

### Converting Gatsby API Files

Now we will convert the Gatsby API files (gatsby-config, gatsby-node, etc.) to use TypeScript. The advantage of this is if the project should grow, it will be nice to have everything type-checked. The other benefit of `.ts` files is that we can use the more modern `import/export` syntax instead of `modules.export/require` syntax.

The issue is, though, these files MUST be in .js for the Gatsby Runner to use them. So, how do we solve this problem?

To start, at the root level of the project, create a folder called `gatsby`.

Copy and paste `gatsby-config.js` & `gatsby-node.js` at the root level into this folder and rename them to `.ts`.

Next, we'll need the following packages: 

- `dotenv` --> Because we will get an ESLint error later on 
 called `import/no-extraneous-dependencies`
- `gatsby-plugin-typescript` --> Allows Gatsby to build TypeScript and TSX files
- `ts-node` --> Will allow us to recognize the TS syntax called from the JS files

Run the command: 

`npm i dotenv gatsby-plugin-typescript ts-node`

Go to `gatsby-config.js` at the root level, select everything and replace it with just these 2 lines:

```javascript
require("ts-node").register();

module.exports = require("./gatsby/gatsby-config");
```

Now, the Gatsby runner will recognize our TypeScript files.

Note, gatsby-config.js at the root level **_MUST_** remain as `.js`. We will be able to switch `gatsby-node` to `.ts` though.

Go `gatsby-config.ts` in the `gatsby` folder, and replace this code:

```javascript
require('dotenv').config({
  path: `.env.${process.env.NODE_ENV}`
});
```

With this code:
```javascript
import dotenv from 'dotenv';

dotenv.config({ path: `.env.${process.env.NODE_ENV}` });
```

Also update the object with the `plugins`, etc., being exported at the bottom from this:

```javascript
module.exports = {
  // ...
};
```

To this:
```javascript
export default {
  // ...
};
```

Make sure to `gatsby-plugin-typescript` to the array of plugins!

Lastly, we need to update the `contentfulConfig` object to include this: `host: process.env.CONTENTFUL_HOST`. If we don't, we get an error down below in the `if` check because we try to access `contentfulConfig.host`, but `host` doesn't exist initially in this variable. So, `contentfulConfig` should look like this:

```javascript
const contentfulConfig = {
  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN || process.env.CONTENTFUL_DELIVERY_TOKEN,
  host: process.env.CONTENTFUL_HOST,
  spaceId: process.env.CONTENTFUL_SPACE_ID
};
```

Now to update `gatsby-node`! As previously mentioned, for the `gatsby-node.js` file at the root level, we can actually rename it to `.ts`. Once you do, select everything and replace it with just this one line: 

`export * from "./gatsby/gatsby-node";`

You will get an error saying something like `this file is not a module`. We just need to update the file to use the `import/export` syntax.

Open `gatsby-node.ts` in the `gatsby` folder, and replace this:

```javascript
const path = require('path');
``` 

With this:

```javascript
import { resolve } from 'path';
```

Next, import the following type from the gatsby package:

```javascript
import type { GatsbyNode } from 'gatsby';
```

Next, update the `createPages` to this:

```javascript
export const createPages: GatsbyNode["createPages"] = async ({ graphql, actions, reporter }) => {
  // ...
};
```

At this point, we should see a type error down below for `const posts = result...` saying: 

`Property 'allContentfulBlogPost' does not exist on type 'unknown'`

We need to set up the type for the result from the GraphQL query. Just outside & above the `createPages` function, create a type called `GraphQLResult`. It will look like this:

```javascript
type GraphQLResult = {
  allContentfulBlogPost: {
    nodes: {
      slug: string;
      title: string;
    }[];
  };
};
```

Next, simply apply this type to the `result` variable and the error should go away:

```javascript
const result = await graphql<GraphQLResult>(
  // ...
);
```

And now another error should appear on `result.data` saying: `Object is possibly 'undefined'`. Just above this line, add the following `if` check and the error should go away:

```javascript
if (!result.data) {
  throw new Error('Failed to get posts.');
}
```

Whew! That was a lot! But now our entire Gatsby project is set up to use TypeScript! 

Let's commit this progress:

`git add . && git commit -m 'updated gatsby api files to use typescript' && git push -u origin main`

## ESLint Setup

Let's add ESLint to our project for some sweet-sweet linting!

To start, run the command: `npx eslint --init`

Answer the questions how you like, but make sure that whichever answers you choose, you make sure to pick the same ones _each time_ you set up ESLint. This way, you can save any custom rules in a separate repo, like I've done [here](https://github.com/andrews1022/eslint-react-quick-setup/blob/main/rules/create-react-app.json), and copy and paste them in. Now, your code will be consistent across all your projects.

This is how I answer the questions:

- How would you like to use ESLint? · `style`
- What type of modules does your project use? · `esm`
- Which framework does your project use? · `react`
- Does your project use TypeScript? · `Yes`
- Where does your code run? · `browser`, `node`
- How would you like to define a style for your project? · `guide`
- Which style guide do you want to follow? · `airbnb`
- What format do you want your config file to be in? · `JSON`

Download any additional packages if prompted. Once done, add in your custom rules if you have any, or you can add them as you go. Then commit this progress: 

`git add . && git commit -m 'added eslint' && git push -u origin main`

## Styled Components Setup

My go-to approach for styling React projects is Styled Components. At first, I didn't really like it. I was used to Sass for styling, and the syntax was weird at first, but after having used it in a few projects, I absolutely love it, and I haven't looked back since.

We'll need the following packages: 

- `react-is` --> Because if we don't, we get an error on Gatsby Cloud saying: `Can't resolve 'react-is' ...`
- `babel-plugin-styled-components`, `gatsby-plugin-styled-components`, & `styled-components` --> These are the packages recommended by Gatsby themselves in their documentation
- `@types/styled-components` --> Needed since `styled-components` don't come with types out of the box

Run the command: 

```
npm i babel-plugin-styled-components gatsby-plugin-styled-components react-is styled-components @types/styled-components
```

Open `gatsby-config.ts` in the `gatsby` folder and add `gatsby-plugin-styled-components` to our plugins array.

### Simple Component Change

Let's make a simple adjustment to the `ArticlePreview` component to make sure everything will work. 

In the `ArticlePreview` folder, create a file called: `styles.ts`

Import styled-components: 

```javascript
import styled from 'styled-components';
```

Open up the CSS modules file. Let's convert the `.article-list` selector to a styled component. Copy and paste this into `styles.ts`:

```javascript
export const ArticleList = styled.ul`
  display: grid;
  grid-gap: 48px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
`;
```

Back in `index.tsx`, add the following import: 

```javascript
import * as S from './styles';
```

I'll explain why I import it this way in just a bit. In the JSX, replace this:

```javascript
<ul className={styles.articleList}>
  // ...
</ul>
```

With this:

```javascript
<S.ArticleList>
  // ...
</S.ArticleList>
```

And if we check the Elements Tab in DevTools, we should see something like:

```javascript
<ul class="styles__ArticleList-bfmZnV jUEOQo">
  // ...
</ul>
```

Of course, the randomly generated class names will be different from what you see here.

So, the reason why I use `import * as S from './styles';`, along with named exports from `styles.ts`, is because it very easily allows me to differentiate styled components from functional components in the JSX. The `S` is just for for `Styled`/. So, you could use `import * as Styled` instead if you would like.

### Adding Global Styles

Now, let's add some global styles to the project. For that we will need 2 things:

- `GlobalStyle` component
- `theme` object

First, let's create the `GlobalStyle` component. Inside the `src` folder, create a new folder called `styles`. In this folder, create a file called `GlobalStyle.ts`. In this file, import `createGlobalStyle`: 

```javascript
import { createGlobalStyle } from "styled-components";
```

Next add this starting code:

```javascript
const GlobalStyle = createGlobalStyle``;

export default GlobalStyle;
```

Inside the backticks is where you can place the global styles you want applied. Let's copy and paste some from `global.css` into there and make the necessary adjustments:

```javascript
const GlobalStyle = createGlobalStyle`
  html {
    scroll-behavior: smooth;
  }

  html * {
    box-sizing: border-box;
  }

  body {
    background: #fff;
    color: #000;
    font-family: 'Inter var', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
    font-size: 16px;
    font-weight: 400;
    line-height: 1.5;
    margin: 0;
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
  }
`;
```

Next, let's create the global theme object. Inside the `styles` folder, create a new file called `theme.ts`, and add this code to start:

```javascript
const theme = {
  mediaQueries: {
    desktopHD: 'only screen and (max-width: 1920px)',
    desktopMedium: 'only screen and (max-width: 1680px)',
    desktopSmall: 'only screen and (max-width: 1440px)',
    laptop: 'only screen and (max-width: 1366px)',
    laptopSmall: 'only screen and (max-width: 1280px)',
    tabletLandscape: 'only screen and (max-width: 1024px)',
    tabletMedium: 'only screen and (max-width: 900px)',
    tabletPortrait: 'only screen and (max-width: 768px)',
    mobileXLarge: 'only screen and (max-width: 640px)',
    mobileLarge: 'only screen and (max-width: 576px)',
    mobileMedium: 'only screen and (max-width: 480px)',
    mobileSmall: 'only screen and (max-width: 415px)',
    mobileXSmall: 'only screen and (max-width: 375px)',
    mobileTiny: 'only screen and (max-width: 325px)'
  },
  colors: {
    red: 'red'
  }
};

export default theme;
```

Now, let's use both of them. To do so, open the `Layout` component file (`src/components/Layout/index.tsx`). In there, import both of these files, along with `ThemeProvider` from `styled-components`:

```javascript
import { ThemeProvider } from "styled-components";
import GlobalStyle from '../../styles/GlobalStyle';
import theme from '../../styles/theme';
```

To use `GlobalStyle`, use it as a component and place it above the Seo component (at the same level). To use `ThemeProvider`, replace the fragment with it. At this point, you should get a red underline. This is because the `ThemeProvider` component expects a `theme` prop. So, we can pass in our `theme` object as the value. In the end, the JSX should look like this:

```javascript
const Layout = ({ children, location }: LayoutProps) => (
  <ThemeProvider theme={theme}>
    <GlobalStyle />
    <Seo title='Gatsby Contentful Blog w/ TypeScript' />
    <Navigation />
    <main className='test'>{children}</main>
    <Footer />
  </ThemeProvider>
);

```

If you've never used Styled Components before, you might be asking _"What does `ThemeProvider` allow us to do?"_ 

When using Styled Components, we automatically get access to `props`, as well as `children`, and we can tap into our `theme` by doing `props.theme`. Let's see an example.

In the `components` folder, create a new folder called `UI`. In this folder I like to store very simple styled components that ONLY affect the UI, such as a `Wrapper` component, or `Copy` component like I showed in an example earlier (of course in this instance it would be purely for styling copy throughout the site), and they can be re-used throughout the project. Think of them like global UI components.

In this starter, a few elements use a `container` class. So, let's create a simple styled component that we can use to wrap JSX elements with.

In the `UI` folder, create a file called `Container.ts`. Since this is a simple styled component, and no JSX is involved, we name it `.ts`.

In the file, add this code:

```javascript
import styled from 'styled-components';

export const Container = styled.div`
  margin: 0 auto;
  max-width: 80rem;
  padding: 24px;
`;
```

Next, let's go to `ArticlePreview/index.tsx`. We can see the starter already has a `Container` component, buuuttt I think the code there is pretty janky, and it's only meant for styling anyways. So, let's replace it with our styled component.

First, let's update our imports:

```javascript
import * as S from './styles';
import { Container } from '../UI/Container';
```

Then simply remove the functional component `Container` being imported to avoid conflicts. Since the name is the same, it will work just like before.

I like to have my styled components imported and exported this way, because I have set rules for myself that:

- Styled components should be named exports
- Functional components should be default exports
- Import everything as `S` from `styles.ts` in the component folder
- Import components from the `UI` folder below it in alphabetical order

I would highly encourage you to create rules like this for yourself. You should do this because then your code will be consistent across all your projects, and when you use the same structure and self-imposed rules, it makes sharing code between your projects a _LOT_ easier. Try new things out here and there, but once you've found what works for you, I would then encourage you to refactor all your existing projects (on your portfolio or not), to use these rules. Think of all the green squares you'll have on GitHub!! But in all seriousness, it shows you care about the quality of your code, which I think is important. And honestly having everything be consistent is just so satisfying.

Ok, now let's use our theme in the `Container`. You may have noticed there is a red color:

```javascript
colors: {
  red: 'red'
}
```

This is just the default red, and it looks terrible, but at least we will know it's working! Simply add this to the styled component:

```javascript
background-color: ${(props) => props.theme.colors.red};
```

Now the `ArticlePreview` component should be wrapped in a glorious red color!

Once you start using styled components, you may notice writing `props.theme` a lot is kind of annoying. Just like with functional components, we can destructure our props inline. So, we can update the `background-color` to be like this:

```javascript
background-color: ${({ theme }) => theme.colors.red};
```

This is optional, but I like doing it this way as I think it's a bit cleaner.

Similarly to functional components, we can set up our own custom props for our styled components and type them as well. For example, let's say we want to have this `Container` component take in a dynamic `backgroundColor` prop, and that value be a `string`. How would we do that?

In `Container.ts`, just above the variable, create a new type `ContainerProps`, and add the following value:

```javascript
type ContainerProps = {
  backgroundColor: string;
}
```

Next, we need to update the styled component to use this type. We can do so like this:

```javascript
export const Container = styled.div<ContainerProps>`
  margin: 0 auto;
  max-width: 80rem;
  padding: 24px;
  background-color: ${({ theme }) => theme.colors.red};
`;
```

Now, we just need to update the component to use `props.backgroundColor` instead of `props.theme`:

```javascript
export const Container = styled.div<ContainerProps>`
  margin: 0 auto;
  max-width: 80rem;
  padding: 24px;
  background-color: ${({ backgroundColor }) => backgroundColor};
`;
```

Now we can pass in a dynamic color to our `Container` each time we use it:

```javascript
return (
  <Container backgroundColor='blue'>
    // ...
  </Container>
)

```

You can take this a step further and set the `backgroundColor` type to only accept certain values. For instance, the `backgroundColor` should only be `red`, `green`, or `blue`:

```javascript
type ContainerProps = {
  backgroundColor: 'red' | 'green' | 'blue';
}
```

Now you should get some sweet auto-completion in VS Code when entering in a value for this prop!

## Done!

At this point, we're done all the setup! Whew! That was a lot! Now, it is up to you to build out your project. Some things you can do from here:

- Add custom fonts (Google Fonts, Adobe Typekit, etc.)
- Add any more desired plugins and/or npm packages
- Convert the remaining existing components using CSS modules to Styled components, or just delete them entirely and start from scratch
- Update GlobalStyle and Theme to your liking

Happy coding!