Gatsby 5
"The great Gatsby" is a line that you would not only get from a novel by F.Scott Fritzgerald, but also if you take interest in the world of Static Site Generators (SSG). Gatsby sets out to be the fastest frontend for the headless web. We are using this technology since 2020, covering experience that starts from version 3 to the current version 5. This tech blog itself is built with gatsby(version 5). Lets share some history and some insights.
What is a static site generator?
A static site generator is a piece of software that takes content from a given source, e.g. a CMS or markdown files and creates HTML pages. In other words, it produces all files of the public facing web interface that are hosted on a simple web server.
The main benefits are:
- no database dependency in the web
- content is separated from the display
- the most secure frontend you can get (files on a plain web server)
- performance optimization at generation time
What is the headless web?
The headless web could be translated as "web without frontend", that means, a simple API for delivering content. A headless architecture greatly simplifies multichannel publishing. While conventional CMS, such as Wordpress or Typo3 are perfect for publishing content to the web, a headless CMS allows using a static site generator to produce the web pages while simultaneously allowing easy access to the content for mobile apps, meta search engines and the like. The content is king and the content is pure with structure like a database model.
The main benefits are:
- structured data to be consumed by software (semantic web)
- decoupled from data rendering to ease maintenance
History of Gatsby
First released in 2015 by Kyle Mathews, it started as an idea to query data like a traditional web page but generate it at compile time (SSG). From the beginning, it was using React - due to flexibility and popularity - to create a mix of static pages that use React to add dynamic "sugar".
By 2018, it was getting popular but it required to team up with Sam Bhagwat and the foundation of Gatsby Inc. to really lift off and bring it to the cloud. They offered enterprise features, i.e. SSG SAAS in the cloud and partnered with Netlify.
In 2023 Netlify acquired Gatsby Inc. It's still Open Source but the future will tell if it will stay like that.
Why do we use gatsby
Every framework has benefits and drawbacks. We decided for gatsby primarily because it was an all in one package and has a good developer experience and community. And back in 2020, it had some unique features in comparison to NextJS (the rival) - dynamic routing, graphql, react, image processing and a strong SEO focus. And the community put so many plugins on the market that it covered almost everything we needed.
Here a two of our successful examples:
1. Scenario Tech Blog
The tech blog itself started as a jekyll, then hugo, then gatsby project. All articles survived each transition because they are stored as markdown files separated from the generator sources. The main purpose is to ease the control of content for developers. The well known version control system based process makes it easy to create branches to add content, merge and review via merge requests and publish everything automatically via a CI pipeline job.
In the tech blog, we started using the "gatsby-starter" project and added the following features:
- Lunr and a matching gatsby plugin to provide a search feature without another server, see Gatsby Lunr Integration
- The manifest plugin, to create a Progressive Web App
- the Image Sharp plugin to optimize pictures regarding size and loading time
- the Gatsby Head API to write SEO tags and semantic web annotation (JSON-LD)
- Typescript, of course
2. Scenario hoch & weit
For a client project, we use Gatsby to create the public facing frontend to give access to thousands of training offer events and informal pages. The informal pages are stored as a ist of structured widget data in a headless CMS. We created a WYSIWYG editor for the users to create pages just like Wordpress or Drupal and store the data in JSON format.
[ { "richText": "<h2>Hochschulen stellen sich vor</h2><br />", "__component": "RichText" }, { "query": "HSP", "__component": "dynamicslider", "sliderItems": [ { "link1": { "link": "www.googol.com", "description": "Link Uni Musterstadt" }, "picture": { "src": "data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22700%22%20height%3D%22500%22%20viewBox%3D%220%200%20700%20500%22%3E%20%3Crect%20fill%3D%22%233C3C3C%22%20width%3D%22700%22%20height%3D%22500%22%2F%3E%20%3Cg%20width%3D%22560%22%20height%3D%22400%22%20transform%3D%22translate%28350%20250%29%22%3E%0A%3Cpath%0Ad%3D%22m%20511.48146%2C205.29524%20c%2011.90567%2C44.43259%2011.90567%2C84.97693%200%2C129.40952%20L%20487.31476%2C349.3424%20270%2C270%20490.20449%2C192.09986%20z%22%0Aid%3D%22path16%22%0Astyle%3D%22fill%3A%23fefe33%22%20%2F%3E%0A%3Cpath%0Ad%3D%22m%20334.70476%2C28.518543%20c%2044.43259%2C11.905676%2079.54502%2C32.17785%20112.07193%2C64.704761%20L%20447.40047%2C118.09589%20270%2C270%20313.06484%2C40.508134%20z%22%0Aid%3D%22path20%22%0Astyle%3D%22fill%3A%23fb9902%22%20%2F%3E%0A%3Cpath%0Ad%3D%22m%20446.77669%2C93.223304%20c%2032.52692%2C32.526916%2052.79909%2C67.639346%2064.70477%2C112.071936%20L%20270%2C270%20z%22%0Aid%3D%22path18%22%0Astyle%3D%22fill%3A%23fabc02%22%20%2F%3E%0A%3Cpath%0Ad%3D%22M%2093.223305%2C93.223305%20C%20125.75022%2C60.696393%20160.86265%2C40.42422%20205.29524%2C28.518543%20L%20231.20546%2C44.501656%20270%2C270%2092.739568%2C120.0571%20z%22%0Aid%3D%22path28%22%0Astyle%3D%22fill%3A%23fe2712%22%20%2F%3E%0A%3Cpath%0Ad%3D%22m%20205.29524%2C28.518543%20c%2044.43259%2C-11.905676%2084.97693%2C-11.905676%20129.40952%2C0%20L%20270%2C270%20z%22%0Aid%3D%22path22%22%0Astyle%3D%22fill%3A%23fd5308%22%20%2F%3E%0A%3Cpath%0Ad%3D%22m%2028.518543%2C334.70476%20c%20-11.905676%2C-44.43259%20-11.905676%2C-84.97693%200%2C-129.40952%20L%2056.311276%2C186.62718%20270%2C270%2055.854788%2C349.40527%20z%22%0Aid%3D%22path26%22%0Astyle%3D%22fill%3A%238601af%22%20%2F%3E%0A%3Cpath%0Ad%3D%22M%2028.518543%2C205.29524%20C%2040.424219%2C160.86265%2060.696393%2C125.75022%2093.223305%2C93.223305%20L%20270%2C270%20z%22%0Aid%3D%22path30%22%0Astyle%3D%22fill%3A%23a7194b%22%20%2F%3E%0A%3Cpath%0Ad%3D%22M%20205.29524%2C511.48146%20C%20160.86265%2C499.57578%20125.75022%2C479.30361%2093.223305%2C446.7767%20L%2095.307837%2C418.58874%20270%2C270%20231.0453%2C499.70648%20z%22%0Aid%3D%22path8%22%0Astyle%3D%22fill%3A%230247fe%22%20%2F%3E%0A%3Cpath%0Ad%3D%22M%2093.223305%2C446.7767%20C%2060.696393%2C414.24978%2040.42422%2C379.13735%2028.518543%2C334.70476%20L%20270%2C270%20z%22%0Aid%3D%22path24%22%0Astyle%3D%22fill%3A%233d01a4%22%20%2F%3E%0A%3Cpath%0Ad%3D%22m%20446.7767%2C446.7767%20c%20-32.52692%2C32.52691%20-67.63935%2C52.79908%20-112.07194%2C64.70476%20L%20310.45335%2C496.38826%20270%2C270%20446.04632%2C421.15701%20z%22%0Aid%3D%22path12%22%0Astyle%3D%22fill%3A%2366b032%22%20%2F%3E%0A%3Cpath%0Ad%3D%22m%20334.70476%2C511.48146%20c%20-44.43259%2C11.90567%20-84.97693%2C11.90567%20-129.40952%2C0%20L%20270%2C270%20z%22%0Aid%3D%22path10%22%0Astyle%3D%22fill%3A%230391ce%22%20%2F%3E%0A%3Cpath%0Ad%3D%22M%20511.48146%2C334.70476%20C%20499.57578%2C379.13735%20479.30361%2C414.24978%20446.7767%2C446.7767%20L%20270%2C270%20511.48146%2C334.70476%20z%22%0Aid%3D%22path14%22%0Astyle%3D%22fill%3A%23d0ea2b%22%20%2F%3E%0A%3Ccircle%20cx%3D%22270%22%20cy%3D%22270%22%20r%3D%22153.79581%22%20id%3D%22circle32%22%2F%3E%0A%3C%2Fg%3E%20%3Ctext%20fill%3D%22%23E9E9E9%22%20font-family%3D%22sans-serif%22%20font-size%3D%2250%22%20dy%3D%2210%22%20font-weight%3D%22bold%22%20x%3D%2250%25%22%20y%3D%2220%25%22%20text-anchor%3D%22middle%22%3EUni%20Musterstadt%3C%2Ftext%3E%20%3Ctext%20fill%3D%22%23E9E9E9%22%20font-family%3D%22sans-serif%22%20font-size%3D%2250%22%20dy%3D%2210%22%20font-weight%3D%22normal%22%20x%3D%2250%25%22%20y%3D%2250%25%22%20text-anchor%3D%22middle%22%3E700%20x%20500%3C%2Ftext%3E%20%3C%2Fsvg%3E" }, "headline": "A headline", "richText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Est, ut dicis, inquam. Cur iustitia laudatur? Sit enim idem caecus, debilis. Verum hoc idem saepe faciamus. Duo Reges: constructio interrete. Quid ad utilitatem tantae pecuniae?", "headlineLevel": 2 } ], "elementsDisplayTime": 10000 } ]
We use a widget component library that is able to render the widget data.
Now, in Gatsby, we have a page template that reads the page data (list of widget data) and uses the widget components to render the content. The result is a simple web page. The thousands of training offers are much easier to render, since it only takes one specialized component and fetching the structured data to create the pages.
While generating the data, we push searchable information into a search service to be used by the generated app at runtime and download files to be available for the user.
In this project, we used a green field approach and ended up using the following features:
- gatsby-plugin-sass to support scss stylesheets
- gatsby-plugin-18n to support multiple languages and separate page definitions
- gatsby-plugin-apollo to have access to graphql at runtime
- gatsby-plugin-matomo to integrate Matomo automatically with the Link API
- gatsby-plugin-sitemap to generate a sitemap automatically
- gatsby-plugin-manifest preparation for PWA
- graphql-codegen to automatically generate typed queries used at runtime (hydrated state)
- gatsby-plugin-next-seo to generate JSON-LD snippets per page to improve SEO
Mentioning SEO: Since data is structured, it's easy to reach very high scores in lighthouse because every page can be adjusted perfectly to use every technical aspect on HTML level.
ContentPageTemplate with SEO:
... return ( <> <Seo title={props.pageContext.title} description={props.pageContext.description} socialTitle={seoTitle} slug={currentSlug} locale={currentLocale} socialDescription={seoDescription}> </Seo> <ArticleJsonLd url={pageUrl} headline={seoTitle} images={[ seoCover.src!, ]} datePublished={site.build.buildTime} dateModified={site.build.buildTime} authorName={site.metadata.publisher} publisherName={site.metadata.publisher} publisherLogo={`${site.metadata.siteUrl}/HRKDFN8.gif`} description={seoDescription} overrides={{ '@type': 'Article', }} /> ... </> )
The bad parts
Plugins
There are some official plugins and there are thousands of community plugins and often in different quality. These range from showcase plugins to CMS vendor specific flavors. This means you have to select the right one and pray that it's updated with every new gatsby version. Right now, the official plugins make up 80% of our plugins. But npm 9.5 went crazy regarding the dependencies of some older plugins - we decided to remove them and had to work around that. The plugin API itself may be worth spending the time, because otherwise it blows up complexity, putting everything into the gatsby-node calls, e.g. downloding files from the CMS or sending page data to a search service. It's possible but you are hacking the lifecycle.
Debugging
On the one hand it's painful to debug the static site generator code (build process), especially after introducing TypeScript. In order to debug the code, some developers use the console.log, but it's much easier using the gatsby documented way, for example using the Chrome debug port (sounds weird but it works). On the other hand, the hydrated client code is as easy to debug as as any react app.
Links and a react component libraries
Gatsby has some special API for Routing (the Link API). If you separate the react components in a separate library, you will have to provide function callbacks or hooks to render the final web site using the Link API.
This is a react component, rendering Links and providing a navigate function. The interface of a component in the library:
export interface LinkComponentProps { navigate?: NavigateFunc | null link?: LinkBundle | null url?: string | null disabled?: boolean | null treatAsAbsolutePath?: boolean | null description?: string | null target?: 'self' | 'blank' | string | null children?: React.ReactNode | null className?: string | null colorSchema?: ColorSchema visualization?: LinkVisualization additionalAttributes?: { [key: string]: string | null | undefined } }
The provided gatsby Link API navigate:
import { navigate } from 'gatsby' export const navigateWithParams = ( slug: string, additionalParams: Map<string, string> = new Map(), ): Promise<void> => { if (typeof window === 'undefined') { return navigate(slug) } const params = new URLSearchParams(window.location.search) additionalParams.forEach((value, key) => { params.set(key, value) }) const paramsAsStrings = params.toString() const fullQueryParamOrEmpty = paramsAsStrings !== '' ? `?${paramsAsStrings}` : '' return navigate(`${slug}${fullQueryParamOrEmpty}`) }
Images and a component library
Although Gatsby has great capabilities to optimize images, you must use it exactly the way it's intended. If you fetch a JSON to render your page content as we do, it's not an easy task to use this API because it relies on the fact that images are available as source directly and you must provide the GatsbyImage just like the Link API via a kind of factory.
We optimized the pictures ourselves when storing them in the CMS and do not use the GatsbyImage (yet).
Summary
Gatsby is a great piece of software and a great novel by F.Scott Fritzgerald. The latter leaves no doubts but the former depends on the usage and skill set.
If you setup your web app with gatsby as framework in mind and stick to the rules, you will have a good time as seen in the tech blog example. It's easy to use, the documentation is superb and everything works already, thanks to plugins. The required skill set contains react, web development and some JAMStack know-how to light the fire.
If you intend to break the rules of how to use gatsby, it will get complicated and some low hanging fruits will not be reached. The case of hoch & weit shows that gatsby can be bent to fit into your architecture. The required skill set, however, is much higher regarding react and gatsby itself.
Image sources
The cover image used in this post was created by Unsplash under the following license. All other images on this page were created by eXXcellent solutions under the terms of the Creative Commons Attribution 4.0 International License