The New Gatsby Homepage Starters - Less Is More
Tags: gatsby, javascript, typescript, contentful
Published: 2022-03-13 16:35:50

GatsbyConf was just last week, March 2nd & 3rd 2022, and it was a blast! A ton of informative talks, workshops, and being able to talk directly with sponsors was something I've never done before and thoroughly enjoyed and I can't wait for it to happen again next year!

During the conference, a couple of things in particular that were announced piqued my interest:

- TypeScript support - between `v4.8` & `v4.9`, we can now have the Gatsby API files (`gatsby-browser`, `gatsby-srr`, `gatsby-config`, and `gatsby-node`) using `.ts` without any extra configuration!
- New CMS starters - new headless CMS starters were released for [Contentful](https://github.com/gatsbyjs/gatsby-starter-contentful-homepage), [DatoCMS](https://github.com/gatsbyjs/gatsby-starter-datocms-homepage), [Drupal](https://github.com/gatsbyjs/gatsby-starter-drupal-homepage), & [WordPress](https://github.com/gatsbyjs/gatsby-starter-wordpress-homepage)!

So, I recently decided to sit down and try out the new Contentful Homepage Starter, as that is the CMS I am most familiar with. And what I found was rather interesting.

I truly enjoy using Gatsby to build Jamstack websites. Integrating a headless CMS like Contentful is so simple to do thanks to Gatsby's plugin ecosystem. From my experience, it basically boils down to:

- Adding the plugin to gatsby-config
- Double checking I entered in the correct API keys

And it just works seamlessly. Gatsby makes it so simple, which I greatly appreciate.

This post outlines my experience with the new Contentful Homepage starter. I am going to provide my honest and upfront feedback and I hope that it is found helpful. If I say anything that is incorrect from a technical perspective, then please let me know and I will absolutely correct that mistake.

## Setup

The setup for the starter was very straightforward. I created a new folder in my projects directory, downloaded the starter using `npx`, ran the `setup` command, entered in my Contentful API keys, started up the local dev server, and I was up and running (more detailed steps can be found on the [GitHub page](https://github.com/gatsbyjs/gatsby-starter-contentful-homepage), of course)!

At this point I will mention that I cleared out my existing Contentful space of all the content and content models I had previously created. It was merely a playground for testing things, and had no real purpose, so I didn't mind. The `setup` script also recommends an empty space too anyways.

Next, I opened the project in VS Code and I noticed the typical starting files and folders for a Gatsby project. However, once I started digging a bit deeper, I noticed quite a few things which I thought were quite atypical. And once I opened Contentful in my browser, I couldn't help but be a bit surprised.

## Contentful Overload

During setup, I noticed it was taking longer than I expected to generate & import the content into Contentful. That's because compared to the [Contentful Gatsby Blog Starter](https://github.com/contentful/starter-gatsby-blog), there is **A LOT** more content being created here. Like, _a lot_. With this new homepage starter, you get in total:

- 33 content models
- 113 pieces of content
- 55 pieces of media (images, icons, etc.)

Compared to the Contentful Gatsby Blog Starter:

- 2 content models
- 4 pieces of content
- 4 pieces of media

That is _quite_ a significant difference. So, why is there so much content at the start here? The answer to this question, I think, can be found at the top of the `README`:

_"This starter demonstrates how to use Contentful to build a homepage and can be customized to match your own visual branding."_

The keyword here, I think, is "_demonstrates_". This is supposed to be a demonstration of how to use Contentful with Gatsby. But after having gone through the code and looking at the abundance of generated content, I found myself trying to answer yet another question:

_"Who is this starter for?"_

The answer to this question, for me, is:

I don't know.

## Generic Content

One glaring issue with the generated content in Contentful is that most of it is tied too heavily to a specific page. With this homepage starter, while we have in total 9 links in the main navigation, we only get 2 fully built pages: Home and About.

Which is fine, no problem there. However, out of the 33 generated content models:

- 17 are for the Homepage (not including the page itself)
- 6 are for the About page (again, not including the page itself)

Combined, that means approximately _70%_ of the content models are tied to specific parts of a specific page. Now, one possible reason for this might be to _very_ explicitly tell devs where everything is _exactly_. However, I feel like there are 2 major issues with this approach, aside from it being rather hand-holding:

- Price
- Reusability

### Price

Let's tackle price first. If you head over to Contentful's [pricing page](https://www.contentful.com/pricing/), you'll quickly realize that Contentful is _**NOT**_ cheap once you move out of the free Community tier. I'll be nice and tell you if you're too lazy to look: it's $490 USD per _month_. Yes, that's right, per _month_. Now, granted that is intended for a team, but still, quite the jump from $0 to $490. Or for me as a Canadian, that is ~$625/month! Granted, again, it is intended for a team and you likely have a source of income to offset the cost.

However, the reason why I bring this up is because with the Community tier, _AND_ with the Team tier actually, you are limited to only 48 different content models. And if you recall, I previously said that this starter comes with 33 content models out of the box. So, that means you are already using up _~68%_ of your total allowed content models. Therefore, this pattern of creating models for specific parts of a specific page simply cannot sustain itself.

### Reusability

This is where the issue of reusability comes in. I think it would be better to create more generic named content models, as it would better highlight their reusability by placing the same types of content on multiple pages throughout the site.

Not only this, but there is some duplication with the existing content models as well. There is `Homepage Stat List` and `About Stat List`. What's the difference here? The difference is on the [Homepage](https://gatsbycontentfulhomepage.gatsbyjs.io/), there is some extra/surrounding content, and on the [About page](https://gatsbycontentfulhomepage.gatsbyjs.io/about/) it is just the group of stats in a row. Why not create a single generic `Stat List` model, and reference a Stat List piece of content either in a different model or just display it in a simple row/grid wherever you want?

I really like some of the existing models, such as `Blog Post`, `NavItemGroup`, `Page`, `SocialLink`, and the 3 `Layout` models (although a minor thing, I would change `LayoutFooter` and `LayoutHeader` to just `Footer` and `Header`. I think we all know what a Footer and Header are). These provide great flexibility and are highly reusable, which is fantastic! Perhaps consider making some more generic models like:

- `CTA`
  - Have fields for the text and the slug where the CTA should take you
- `Author`
  - Have this as a reference field for `Blog Post`
- `[SOMETHING] Group`
  - In this model you reference as many of the individual content models as you like
  - For example, `Stat Group`. You then have a field to reference as many individual `Stats` as you like
  - Now you have created a single Group which can be reused wherever you like
  - Do the same for things like `Cards` (Icon, Heading, Copy, Link), `Team Members` (Picture, Name, Role in Company), and more

So, I think to truly show the power of Gatsby + Contentful, it would be wise to consider:

- Creating more generic pieces of content
- Building out more of the pages within the starter using these pieces of content

The benefits of this are:

- I can see how reusable pieces of content can be
- I can change which pieces of content I want to show on a given page (using the `Page` model) that were already in place, or I can add new ones that I create

Ultimately, I think there is too much content being created at the start here. There is, I think, a pretty decent chance you will end scrapping a decent portion of these models and content. Or, you will have to spend a lot of time restructuring/renaming it to meet your project's needs, which is not ideal. On the other hand, the existing [contentful/starter-gatsby-blog](https://github.com/contentful/starter-gatsby-blog) I think has too little content. Therefore, I think there needs to be a nice middle ground with the quantity of content being generated out of the box.

Lastly, while I like the idea behind the About Page and Homepage models, this isn't exactly ideal. Due to the limitation of the number of content models you can create, this pattern will not be able to continue much longer (creating models for individual pages). Something I have tried to do before is create a single `Page` model and render that model using a single template file in the code. This model would have fields like:

- `Title`
- `Slug` (automatically generated based off the `Title`)
- `Content` or `Sections` (reference as many pieces of content as you wish. Things like 50/50's, Groups, Hero's, etc., to structure the various blocks/sections for a page as needed)

Some issues with this approach though are:

- This gets kinda sloppy with GraphQL, as you have 1 _really_ large query at the bottom of the template file. I suppose you could try breaking it up with fragments to make it a bit cleaner, though
- You need to make sure to have a `ComponentToRender` field on all of the models you wish to use as a section

You can see my approach [here](https://github.com/andrews1022/contentful-blog-gatsby-starter/blob/dynamic-nav-and-page/src/templates/dynamic-page.tsx) (at the time I already had a `Page` model, so I named it `Dynamic Page` just for testing as an FYI). We can use `React.lazy()` to dynamically import and render each component based on the value of the `ComponentToRender` field, and pass along the `content` as a prop. You then set the styles/props/types/etc. for each component as you normally would.

I like this approach as it allows you to create a single template for (theoretically) an endless number of pages. Again, the GraphQL query would become incredibly long and rather difficult to maintain.

Another drawback to this setup though is that the workflow has a lot of steps that you have to do each time:

- Create the model
- Ensure the model has a `ComponentToRender` field (you can set a default value to save you some time from entering in a value each time you create a `Page`)
- Create/style/type the component
- Add it to the long GraphQL query in the template file

Where I do think this approach _might_ come in handy is if you have a parent page, say `Services`, and then you have multiple pages for each `Service` where the order of the sections on each page is the exact same, just that the content is different. So, you can create a single template like I did, and this ensures that the GraphQL query is of a certain length, remains consistent, and is easier to maintain.

## The Code

Regarding the code of this starter, I'm just going to list a few things I noticed and outline some areas that I think could be improved:

### Organize scripts in `package.json`

This is something that annoys me a bit not with this starter, but most projects in general. Put your scripts in alphabetical order! It makes it so much easier to find/edit/update/add scripts to the list.

### Organize `dependencies` in `package.json`

I've personally never understood why we need both `dependencies` _AND_ `devDependencies`. Either way, you depend on that package. Why the separation? It seems highly redundant to me. Why not group all of them together into a single `dependencies` object and order them alphabetically to keep things nice and tidy?

Create React App made the decision to consolidate everything to into `dependencies` a few years agi, and you can see Dan Abramov's explanation [here](https://stackoverflow.com/questions/44868453/create-react-app-install-devdepencies-in-dependencies-section/#answer-44872787). I of course agree with the reasons he provided, but in all reality, if you want to move packages out of `dependencies` into `devDependencies`, you are more than free to do so.

### Organize components into their own folders

This starter comes with 22 different component files. I understand everyone has their preferred approach for organizing components (and that may be why things are as they are as devs will move things around as they please anyways), but a common approach I see mentioned online often is:

```
src/
  components/
    ComponentA/
      ComponentA.js - The actual React component
      ComponentA.styles.js - The Styled Components file for the component (could also be .css or .scss module, of course)
      ComponentA.test.js - The tests for the component
      ComponentA.stories.js - The Storybook file for the component
    ComponentB/
      ComponentB.js
    ComponentC/
      ComponentC.js
    etc.
```

I think when you have this many components all grouped together, it looks disorganized. Gatsby is an opinionated framework, and therefore it makes sense to have an opinion on how components should be organized. But if this is their opinion on how things should be organized, having nothing in folders, then that is pretty disappointing, and I would strongly encourage Gatsby to reconsider.

Lastly, I noticed there was a `Fallback` component being used in both the `home` and about `page` files. I think it would be wise to create a single `Fallback` component in the `components` folder and import it from there to cut down on duplication of code.

### UI Components

In addition to the point above, there is a file in the components folder called `ui.js`. In this one file, there are **28** UI components. Now, having all your components all in a single file should make it easier to maintain, in theory. In practice, this also feels highly disorganized. I think it's best to keep a 1:1 ratio: 1 component to 1 file. So, the structure would then ideally be something like this:

```
src/
  components/
    ...
    ui/
      Base
      Container
      Flex
      etc.
```

The import would then look something like:

`import Base from 'components/ui/Base'`

Which I think is totally fine! This is similar to how you [import components from Material UI](https://mui.com/api/button/), a popular React UI library.

Finally, I found the code in `ui.js` difficult to understand. I'm not entirely sure what is going on for some of the components, specifically the `cx` & `Base` components. I think it would be helpful to at least include some comments noting what their purpose is and/or how they work.

### Conditional Rendering

I would suggest not using `&&` for conditional rendering. You should be using the ternary operator with the desired 'or' case, or just return `null`. The reasons why are explained [here](https://kentcdodds.com/blog/use-ternaries-rather-than-and-and-in-jsx) by Kent C. Dodds in a blog post.

### Spacing

A lot of the code is not spaced enough, I think. For example, the component [`caret.js`](https://github.com/gatsbyjs/gatsby-starter-contentful-homepage/blob/main/src/components/caret.js).

I think there should be a line break between the imports and the function, as well as a line break between `const height ...` and `return`. I personally find that when code is spaced out a bit, it makes it much easier to read.

### Custom Hooks

I would like to suggest moving the uses of `useStaticQuery` into their own hooks. For example, the `footer` and `header` components. Extract these hook calls out and wrap them in their own custom hooks, such as `useFooter()` and `useHeader()`. The benefits of this are that it shows devs how to use custom hooks if they have little or no experience doing so before, and it makes the function code a lot leaner and easier to read.

### Props Spreading

I would like to strongly encourage prop spreading to be removed where it is being used. The reason for this I think is explained perfectly on the `react/jsx-props-no-spreading` [ESLint page](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-props-no-spreading.md):

"_Enforces that there is no spreading for any JSX attribute. This enhances readability of code by being more explicit about what props are received by the component. It is also good for maintainability by avoiding passing unintentional extra props and allowing React to emit warnings when invalid HTML props are passed to HTML elements._"

This leads into my next point, which is regarding TypeScript.

### TypeScript

As mentioned near the beginning of this post, between Gatsby `v4.8` & `v4.9`, we can now have the Gatsby API files using `.ts` without any extra configuration. Which is fantastic! I was so happy to see this update!

However, with the heavy use of props spreading, I think it might lead to some frustration/confusion over how to type each of the components props, as well as typing out values returned via GraphQL queries (although, there are ways of doing the latter automatically with tools like [GraphQL Code Generator](https://www.graphql-code-generator.com/)).

Furthermore, it would be great to have an all TypeScript version of not only this starter, or these new homepage starters, but for all official Gatsby starters. As far as I can tell, this is only true for `gatsby-starter-minimal`. And while we can have our entire Gatsby project using TypeScript, we still do not get type checking in the CLI. Hopefully this comes in a future release.

### gatsby-config

A minor point, but in `gatsby-config.js`, there is `require('dotenv').config()` twice, one with and one without an `options` object argument. Not sure why that is the case, as the second one with the `options` object being passed in is perfectly fine on its own.

Also, if you implement ESLint into your project, you will get the `import/no-extraneous-dependencies` error. This error appears when you are using a specific package not listed in your `package.json`. Gatsby uses `dotenv` under the hood, so everything will work just fine, but I think it is best to include it in `package.json`.

### Optional

Now, just a short list of some minor things you may or may not be looking to do, or have to do, depending on your/your teams workflow:

- Replace `react-feather` with something like Font Awesome or Material UI Icons, or another icon library
- Replace `@vanilla-extract` with `styled-components`, `emotion`, or other CSS-in-JS library, or `.css`/`.scss` modules
- Convert all components to use function expression syntax if that is your preference
- Change `import * as React from 'react';` to `import React from 'react';`
  - This way you can stop using `React.something()`, and just import what you need (ie. hooks): `import React, { useState } from 'react';`

## Going Forward

So, let's try and answer this question from before:

_"Who is this starter for?"_

Based off the time I have spent with this, I believe the answer to this question is a developer who is:

- New to Gatsby (or React in general)
- New to not just Contentful, but building Jamstack apps/using headless CMSs in general

Now, there is nothing wrong with creating a starter for that type of developer. However, if you meet that criteria, you might:

- Find the amount of pre-generated content overwhelming
- Like that content models are separated by page. However, as outlined earlier, both the Community and Team tiers for Contentful have a limit to how many models you can create, and Contentful is not cheap

If you are an experienced dev, and have your own preferred workflow, you will find yourself spending a lot time initially updating/refactoring both the code and the content in Contentful.

If you work for an agency, you/your team may or may not:

- Have your own starter that you may or may not maintain
- Have a go-to list of NPM packages for certain functionality. Therefore, you may or may not have to heavily modify this starter
- Take this starter and modify it to your teams workflow, and have your own custom starter
  - Or you can start from scratch each time for a new project/client. But if you create your own custom starter, you then have to maintain it / update it

So, how do we move forward from here?

I think there are a few options to consider:

- Refactor this starter both content and code wise
- Create slightly different versions of these starters with varying quantities of content for varying skill levels
- Make TypeScript only versions of these starters

In the end, these are just my opinions and my opinions alone. Gatsby could absolutely just ignore what some random person is saying about them on the internet. However, I do truly hope you’ve found the opinions I have expressed here in this post at least a little helpful. And even if Gatsby took 1 small, minor thing I've mentioned here and implemented it, I would take immense pride in knowing that. If anyone from the Gatsby team would like to get in touch with me to discuss any of the points I've made, or if I can assist in any other way, you can reach out to me on [Twitter](https://twitter.com/andrew_devsrc) and I would be more than happy to discuss things there.

Thank you for reading.