<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Patrick Sevat</title>
    <description>The latest articles on DEV Community by Patrick Sevat (@patricksevat).</description>
    <link>https://dev.to/patricksevat</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F340570%2F77d55f9e-b2aa-483d-a8dd-616b805fcdfe.jpeg</url>
      <title>DEV Community: Patrick Sevat</title>
      <link>https://dev.to/patricksevat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/patricksevat"/>
    <language>en</language>
    <item>
      <title>Optimize your git clone / fetch strategy for CI pipelines</title>
      <dc:creator>Patrick Sevat</dc:creator>
      <pubDate>Sat, 26 Sep 2020 21:33:38 +0000</pubDate>
      <link>https://dev.to/patricksevat/optimize-your-git-clone-fetch-strategy-for-ci-pipeline-3pka</link>
      <guid>https://dev.to/patricksevat/optimize-your-git-clone-fetch-strategy-for-ci-pipeline-3pka</guid>
      <description>&lt;p&gt;Over the last week I've been working hard on migrating our &lt;a href="https://medium.com/developing-senses"&gt;Frontend Monorepo&lt;/a&gt; from a on-premise Jenkins pipeline to a cloud based solution.&lt;/p&gt;

&lt;p&gt;One of the big obstacles I had to overcome was dealing with the sheer size of our monorepo. If we include the whole history (all branches on remote, all tags, full history), the size of our repo amounts to more than 1GB.&lt;/p&gt;

&lt;p&gt;Downloading that much data on each run is a waste of bandwith and time, so I've dived into the git documentation and found several possibilities that I will share with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  You only need to checkout / build a single branch
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;git clone --single-branch&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This simple flag (&lt;code&gt;--single-branch&lt;/code&gt;) makes sure you only fetch the history of your main branch. The more active branches live on your remote, the bigger the benefit and with more than 400 active contributors in our repo, that helps!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;git fetch --no-tags&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Another simple flag! This one omits the tags from the git history. Tags are often version numbers such as &lt;code&gt;&amp;lt;package-name&amp;gt;@1.2.67&lt;/code&gt;. Although not a huge data saver, it helps. Be careful though, some CI pipelines need tags, so double check if you need it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;git fetch --depth=&amp;lt;n&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Oh yes, now we are talking. This flag restricts the history to the latest  commits. Really useful if you only care about the newest state of a certain branch. For example if you know you need to build a feature branch (or your main branch) and you don't need all it's history! Potential huge data savings 🤤.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If your depth is quite large, the server will have to do a lot of calculations increasing transfer time. A depth of &lt;code&gt;50000&lt;/code&gt; increased time by 300%. A depth of &lt;code&gt;1&lt;/code&gt; cut time with 90%.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;git fetch --depth=1 origin &amp;lt;full_SHA1&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Used when you need a single commit that is not at the tip of a branch, without pulling in all history of that branch. See &lt;a href="https://stackoverflow.com/questions/14872486/retrieve-specific-commit-from-a-remote-git-repository"&gt;this StackOverflow question for details&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Combining the flags &lt;code&gt;git clone --no-tags --single-branch --depth=1&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If you just need the latest state of a single branch this is your command! You can also modify this command to checkout a specific branch by adding &lt;code&gt;--branch=&amp;lt;branch-name&amp;gt;&lt;/code&gt; if you are building a branch that is not your main.&lt;/p&gt;

&lt;h2&gt;
  
  
  You need to more than one branch (but not all)
&lt;/h2&gt;

&lt;p&gt;This is where things become a bit more tricky. In this scenario &lt;code&gt;git clone&lt;/code&gt; might not be our best bet because it only allows for cloning &lt;em&gt;all&lt;/em&gt; branches or &lt;em&gt;one&lt;/em&gt; branch.&lt;/p&gt;

&lt;p&gt;This was also the scenario which was most tricky for me, because I needed to determine a single "ancestor" commit where a feature branch started to branch of the main branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;git clone --depth=&amp;lt;n&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This command fetches all active branches with limited history. This might work for you if you are interested in the most recent state of two (or more branches).&lt;/p&gt;

&lt;p&gt;Downsides of this command is that it still fetches &lt;em&gt;all&lt;/em&gt; branches and that determining "ancestor" commits is difficult/impossible (depends on depth, but can still be inaccurate).&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom git config
&lt;/h3&gt;

&lt;p&gt;This one might seem scary, but can actually be really effective. You have full control over the number of branches you want to track, if you would like tags and still control depth of the history&lt;/p&gt;

&lt;h4&gt;
  
  
  Steps
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Initialize an empty git repository by running &lt;code&gt;git init&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following snippet to your &lt;code&gt;.git/config&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# .git/config
&lt;/span&gt;&lt;span class="err"&gt;[core]&lt;/span&gt;
        &lt;span class="nv"&gt;repositoryformatversion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 0
        &lt;span class="nv"&gt;filemode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
        &lt;span class="nv"&gt;bare&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
        &lt;span class="nv"&gt;logallrefupdates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="err"&gt;[remote&lt;/span&gt; &lt;span class="err"&gt;\"origin\"]&lt;/span&gt;
        &lt;span class="c"&gt;# replace with your own repo!
&lt;/span&gt;     &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; git@github.com:patricksevat/foo.git

        &lt;span class="c"&gt;# list the branches that you want to track
&lt;/span&gt;     &lt;span class="nv"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +refs/heads/main:refs/remotes/origin/main
        &lt;span class="nv"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +refs/heads/development:refs/remotes/origin/development
        &lt;span class="nv"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; +refs/heads/feature:refs/remotes/origin/feature

&lt;span class="c"&gt;# Replace main with master or whatever your main branch name is
&lt;/span&gt;&lt;span class="err"&gt;[branch&lt;/span&gt; &lt;span class="err"&gt;\"main\"]&lt;/span&gt;
        &lt;span class="nv"&gt;remote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; origin
        &lt;span class="nv"&gt;merge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; refs/heads/main
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now you can &lt;code&gt;git fetch&lt;/code&gt; with whatever flags you like. &lt;br&gt;
Do not add any flags for full history of the listed branches, or use &lt;code&gt;--no-tags&lt;/code&gt; or &lt;code&gt;--depth=1&lt;/code&gt; to limit history.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't forget to run &lt;code&gt;git checkout&lt;/code&gt; with one of your fetched branches or you'll have an empty working directory!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope these commands help you in optimizing your own git CI strategy. If you have any useful tips or improvements please leave them in the comments!&lt;/p&gt;

</description>
      <category>git</category>
      <category>ci</category>
      <category>pipeline</category>
    </item>
    <item>
      <title>How to use Web Components in HTMLTemplateElement in Angular</title>
      <dc:creator>Patrick Sevat</dc:creator>
      <pubDate>Sun, 19 Jul 2020 22:14:56 +0000</pubDate>
      <link>https://dev.to/patricksevat/using-web-components-custom-elements-in-template-tag-htmltemplateelement-using-angular-1ik8</link>
      <guid>https://dev.to/patricksevat/using-web-components-custom-elements-in-template-tag-htmltemplateelement-using-angular-1ik8</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;[innerHTML]&lt;/code&gt; on the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;DomSanitizer.bypassSecurityTrustHtml()&lt;/code&gt; to make sure your Custom Elements are not stripped from your provided HTML string&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See code example at the end&lt;/p&gt;




&lt;p&gt;Within the Rabobank Design System (based on Web Components) we sometimes come across with unusual specs. Our department relating to wholesale banking (large business customers) came up with the requirement of a select dropdown containing thousands of bank accounts.&lt;/p&gt;

&lt;p&gt;We also envisaged different use cases than just thousands of bank accounts. Maybe thousands of checkboxes with labels and icons. To allow for future use cases we wanted to leverage the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; component, also known as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement"&gt;&lt;code&gt;HTMLTemplateElement&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The cool thing about the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag is that its contents are not actually rendered by the browser as long as they reside within &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;. This would give us the flexibility we need for our component.&lt;/p&gt;

&lt;p&gt;Our components worked fine in plain HTML / JS but once we appended the components within our &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag to the DOM using Angular, it started double rendering! 😠&lt;/p&gt;

&lt;p&gt;When inspecting the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element we also noticed that in Angular it did not yield a new &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment"&gt;DocumentFragment&lt;/a&gt; as it does in plain HTML...&lt;/p&gt;

&lt;p&gt;This means the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag was not recognized as such and because our Web Components used slots, those slots where rendered and then re-rendered upon appending to the DOM.&lt;/p&gt;

&lt;p&gt;Unfortunately, searching Google for &lt;code&gt;angular&lt;/code&gt; + &lt;code&gt;template&lt;/code&gt; only yields results for &lt;code&gt;ng-template&lt;/code&gt;, but after searching for &lt;code&gt;Angular HTMLTemplateElement&lt;/code&gt; we got to &lt;a href="https://stackoverflow.com/questions/51925660/angular-6-create-htmlelement-from-htmltemplateelement"&gt;this StackOverflow question&lt;/a&gt;, which point us to the &lt;code&gt;[innerHTML]&lt;/code&gt; syntax.&lt;/p&gt;

&lt;p&gt;After trying binding to the &lt;code&gt;innerHTML&lt;/code&gt; property we noticed that the double rendering stopped, but the Web Components within the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag were not rendering as they should, but with an example consisting of HTML5 elements (&lt;code&gt;span&lt;/code&gt;, &lt;code&gt;div&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt;...) it did render as expected.&lt;/p&gt;

&lt;p&gt;There were two possible explanations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Web Components were not registered correctly.

&lt;ul&gt;
&lt;li&gt;A quick inspection of the &lt;code&gt;CustomElementRegistry&lt;/code&gt; showed that they were registered&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;There is some sort of sanization in play that strips out "invalid" elements because of the usage of &lt;code&gt;innerHTML&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The sanitation turned out to be the culprit. By using the &lt;a href="https://angular.io/api/platform-browser/DomSanitizer"&gt;&lt;code&gt;DomSanitizer&lt;/code&gt;&lt;/a&gt; we were able to mark our HTML as safe and get our code working as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* some-component.component.ts */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DomSanitizer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/platform-browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;some-component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./some-component.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;SomeComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_sanitizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DomSanitizer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="nx"&gt;templateHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_sanitizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bypassSecurityTrustHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
        &amp;lt;webcomponent-bank-account&amp;gt;
            &amp;lt;span slot="label"&amp;gt;John Doe&amp;lt;/span&amp;gt;
            &amp;lt;span slot="balance"&amp;gt;€ 100.000&amp;lt;/span&amp;gt;
            &amp;lt;p slot="account-number"&amp;gt;1234567890&amp;lt;/p&amp;gt;
        &amp;lt;/webcomponent-bank-account&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- some-component.component.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;#webComponentTemplate&lt;/span&gt; &lt;span class="na"&gt;[innerHTML]=&lt;/span&gt;&lt;span class="s"&gt;"templateHtml"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>webcomponents</category>
      <category>angular</category>
      <category>htmltemplateelement</category>
    </item>
    <item>
      <title>Wrapping up: useful plugins for Gatsby</title>
      <dc:creator>Patrick Sevat</dc:creator>
      <pubDate>Thu, 27 Feb 2020 21:42:40 +0000</pubDate>
      <link>https://dev.to/patricksevat/wrapping-up-useful-plugins-for-gatsby-54dg</link>
      <guid>https://dev.to/patricksevat/wrapping-up-useful-plugins-for-gatsby-54dg</guid>
      <description>&lt;p&gt;&lt;em&gt;Illustration by Katerina Limpitsouni &lt;a href="//undraw.co"&gt;undraw.co/illustrations&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently I started migrating an older Drupal based blog to a combination of Gatsby and Netlify CMS (&lt;a href="https://github.com/patricksevat/de-europeanen-jamstack"&gt;source&lt;/a&gt;), both of which I had no prior experience with. In this blog series I'll talk you through the experiences, hurdles and solutions. In part 6 I'll give some advice how to implement common features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typescript in Gatsby
&lt;/h2&gt;

&lt;p&gt;My starter template did not provide Typescript out of the box, luckily there's a plugin for that 🎉. Just install &lt;code&gt;gatsby-plugin-typescript&lt;/code&gt; and add it to your plugin array. You can now either rename your &lt;code&gt;.js(x)&lt;/code&gt; files to &lt;code&gt;.ts(x)&lt;/code&gt; with no further config needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search functionality in Gatsby
&lt;/h2&gt;

&lt;p&gt;This would be one of the domains where beforehand I'd expect GraphQL to excel, however, GraphQL is not available in the browser... d'oh!&lt;/p&gt;

&lt;p&gt;To get search working browser side there are 2 approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;API based using Algolia and &lt;a href="https://www.gatsbyjs.org/packages/gatsby-plugin-algolia/"&gt;its Gatsby plugin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Creating an index and querying in the browser ("local search"). I took this approach and used the &lt;code&gt;@gatsby-contrib/gatsby-plugin-elasticlunr-search&lt;/code&gt; plugin.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;❗ Be aware that for websites with lots of websites the created index can get bloated really, really fast. Keep an eye on your bundle size!&lt;/p&gt;

&lt;h2&gt;
  
  
  Pagination in Gatsby
&lt;/h2&gt;

&lt;p&gt;For my "all blogs page" I wanted to add pagination. For this to work you do not need an external plugin, but you can follow the approach described on the &lt;a href="https://www.gatsbyjs.org/docs/adding-pagination/"&gt;Gatsby docs&lt;/a&gt;. A quick gist from &lt;code&gt;gatsby-node.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    # big GraphQL query
  `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allMarkdownRemark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blogs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;templateKey&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog-post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blogsPerPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;numPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blogs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;blogsPerPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;numPages&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;createPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`/blog`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/blog/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/templates/blog-overview.tsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;postsPerPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;postsPerPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;numPages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// more code&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Styling SVG in Gatsby
&lt;/h2&gt;

&lt;p&gt;Using SVG files &lt;a href="https://www.gatsbyjs.org/docs/importing-assets-into-files/"&gt;works out of the box in Gatsby via Webpack&lt;/a&gt;. However, the drawback is that Webpack converts smaller SVG files into a data-URI. This works fine, but the downside is that you can no longer target the SVG file in css, for example to change the &lt;code&gt;fill&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;To overcome this I've installed &lt;code&gt;gatsby-plugin-react-svg&lt;/code&gt; plugin which I've configured to include this RegEx &lt;code&gt;/\.inline\.svg$/&lt;/code&gt;;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Paperclip&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../img/paperclip-logo.inline.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FooComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Paperclip&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;an paperclip icon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Image optimalization in Gatsby
&lt;/h2&gt;

&lt;p&gt;One of the most useful plugins that came with the &lt;a href=""&gt;Gatsby + Netlify CMS starter template&lt;/a&gt; are the &lt;a href="https://www.gatsbyjs.org/packages/gatsby-transformer-sharp/"&gt;&lt;code&gt;gatsby-transformer-sharp&lt;/code&gt; and &lt;code&gt;gatsy-plugin-sharp&lt;/code&gt; plugins&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These plugin autogenerate images in the newer &lt;code&gt;webp&lt;/code&gt; format which takes less size while retaining quality. Furthermore, it can also generate smaller versions of images if you know that full-size is not needed (for example in a card component). And for places where you do want larger sizes, the plugins can also create an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-srcset"&gt;&lt;code&gt;srcset&lt;/code&gt;&lt;/a&gt; which allows browsers to pick the most appropriate size.&lt;/p&gt;

&lt;h2&gt;
  
  
  PWA support in Gatsby
&lt;/h2&gt;

&lt;p&gt;We all want that 100 in &lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;Lighthouse&lt;/a&gt;, right? To get the 100 in PWA we must enable two plugins: &lt;a href="https://www.gatsbyjs.org/packages/gatsby-plugin-manifest"&gt;&lt;code&gt;gatsby-plugin-manifest&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://www.gatsbyjs.org/packages/gatsby-plugin-offline"&gt;&lt;code&gt;gatsby-plugin-offline&lt;/code&gt;&lt;/a&gt;. Further reading can be done &lt;a href="https://www.gatsbyjs.org/docs/progressive-web-app/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Big thanks to all readers ❤! I'd love to hear your thoughts in the comments or via Twitter!&lt;/p&gt;




&lt;p&gt;This blog is part of a series on how I migrated away from a self-hosted Drupal blog to a modern JAM stack with Gatsby and Netlify CMS.&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>So... what database does Gatsby use?</title>
      <dc:creator>Patrick Sevat</dc:creator>
      <pubDate>Thu, 27 Feb 2020 21:42:26 +0000</pubDate>
      <link>https://dev.to/patricksevat/so-what-database-does-gatsby-use-545f</link>
      <guid>https://dev.to/patricksevat/so-what-database-does-gatsby-use-545f</guid>
      <description>&lt;p&gt;Recently I started migrating an older Drupal based blog to a combination of Gatsby and Netlify CMS (&lt;a href="https://github.com/patricksevat/de-europeanen-jamstack"&gt;source&lt;/a&gt;), both of which I had no prior experience with. In this blog series I'll talk you through the experiences, hurdles and solutions. In part 5 I'll answer one of the questions that kept nagging me during development: what database does Gatsby use to query from GraphQL?&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL queries from databases right?
&lt;/h2&gt;

&lt;p&gt;In my previous understanding, one of the defining features of GraphQL is being able to query efficiently from multiple datasources. I always understood these datasources can be REST APIs, other GraphQL APIs or databases such as SQL and MongoDb.&lt;/p&gt;

&lt;p&gt;Thus arised my question, what database does GatsbyJS use?&lt;/p&gt;

&lt;h2&gt;
  
  
  No database, but Redux
&lt;/h2&gt;

&lt;p&gt;Gatsby actually does not use a database at all. Under the hood everything that is processed by the plugins gets &lt;a href="https://www.gatsbyjs.org/docs/data-storage-redux/"&gt;transformed into &lt;code&gt;Nodes&lt;/code&gt;&lt;/a&gt; and added to a nodes collection in &lt;a href="https://redux.js.org/"&gt;Redux&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Other collections that exist in Redux are: &lt;code&gt;pages&lt;/code&gt;, &lt;code&gt;components&lt;/code&gt;, &lt;code&gt;schema&lt;/code&gt;, &lt;code&gt;jobs&lt;/code&gt;, &lt;code&gt;webpack&lt;/code&gt; and &lt;code&gt;metadata&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The combination of plugins and actions (such as those defined in &lt;code&gt;gatsby-node.js&lt;/code&gt; like &lt;code&gt;createPage()&lt;/code&gt;) will populate these collections.&lt;/p&gt;

&lt;p&gt;Redux stores these collections &lt;em&gt;in-memory&lt;/em&gt; during build / dev time and GraphQL queries against these collection based on an &lt;a href="https://www.gatsbyjs.org/docs/schema-generation/"&gt;automatically generated GraphQL Schema&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The in-memory approach also means that data is &lt;em&gt;not persisted&lt;/em&gt;! A regular database would write its data to a database file on a system. In-memory databases recreate the whole database every time. Gatsby does provide a &lt;a href="https://www.gatsbyjs.org/docs/build-caching/"&gt;caching system&lt;/a&gt; to speed up subsequent builds though.&lt;/p&gt;

&lt;p&gt;The schema itself is also stored in Redux &lt;a href="https://github.com/gatsbyjs/gatsby/blob/561d33e2e491d3971cb2a404eec9705a5a493602/packages/gatsby/src/query/query-runner.js#L28-L36"&gt;and used to execute GraphQL queries that query other Redux collections&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's quite some Redux-ception!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bijsDVtr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wu4l05im0gdw8rhk0pw6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bijsDVtr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wu4l05im0gdw8rhk0pw6.gif" alt="Alt Text" width="405" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is still one problem to be solved. Redux collections store plain Javascript, but GraphQL queries are written in using the GraphQL Syntax. The solution is using &lt;a href="https://github.com/crcn/sift.js/tree/master"&gt;sift.js&lt;/a&gt;. Further reading can be found &lt;a href="https://www.gatsbyjs.org/docs/schema-sift/#summary"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The in-memory approach does have some &lt;a href="https://www.gatsbyjs.org/docs/scaling-issues/"&gt;drawback at scale (100k+ documents)&lt;/a&gt;...&lt;/p&gt;

&lt;h2&gt;
  
  
  Next up, LokiJS
&lt;/h2&gt;

&lt;p&gt;To address the scaling issues, the Gatsby team are investigating replacing Redux with the &lt;a href="http://techfort.github.io/LokiJS/"&gt;LokiJS library&lt;/a&gt;. This storage / retrieval mechanism is &lt;a href="https://www.gatsbyjs.org/docs/scaling-issues/#gatsby_db_nodes"&gt;still hidden behind a feature flag&lt;/a&gt;. LokiJS is still an in-memory database, but promises better performance. &lt;/p&gt;

&lt;p&gt;If you'd like to know more, take a peek in the &lt;a href="https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/db/nodes.js"&gt;source code&lt;/a&gt; and follow the code 🔎.&lt;/p&gt;

&lt;p&gt;That's it! Hopefully you understand now where the data comes from that is being queried by GraphQL!&lt;/p&gt;




&lt;p&gt;This blog is part of a series on how I migrated away from a self-hosted Drupal blog to a modern JAM stack with Gatsby and Netlify CMS.&lt;/p&gt;

</description>
      <category>gatsby</category>
    </item>
    <item>
      <title>How to retrieve all fields of a Netlify CMS Relation in Gatsby</title>
      <dc:creator>Patrick Sevat</dc:creator>
      <pubDate>Thu, 27 Feb 2020 21:42:10 +0000</pubDate>
      <link>https://dev.to/patricksevat/how-to-retrieve-all-fields-of-a-netlify-cms-relation-in-gatsby-5b36</link>
      <guid>https://dev.to/patricksevat/how-to-retrieve-all-fields-of-a-netlify-cms-relation-in-gatsby-5b36</guid>
      <description>&lt;p&gt;&lt;em&gt;Illustration by Katerina Limpitsouni &lt;a href="//undraw.co"&gt;undraw.co/illustrations&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently I started migrating an older Drupal based blog to a combination of Gatsby and Netlify CMS (&lt;a href="https://github.com/patricksevat/de-europeanen-jamstack"&gt;source&lt;/a&gt;), both of which I had no prior experience with. In this blog series I'll talk you through the experiences, hurdles and solutions. In part 4 I'll explain Netlify CMS relations and how to make sure you can retrieve all fields of the linked relation.&lt;/p&gt;

&lt;p&gt;❗ This blogs assumes basic understanding of Gatsby and GraphQL queries, read &lt;a href="https://dev.to/patricksevat/a-beginners-guide-to-understanding-your-gatsby-starter-template-4f1f"&gt;this blog&lt;/a&gt; first if you do not feel comfortable yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a relation in Netlify CMS
&lt;/h2&gt;

&lt;p&gt;If you have read &lt;a href="https://dev.to/patricksevat/a-beginners-guide-to-setting-up-netlify-cms-343a-temp-slug-6341293"&gt;my previous blog&lt;/a&gt;, you've set up your content collections and fields by now.&lt;/p&gt;

&lt;p&gt;One of the widgets I really like is the &lt;a href="https://www.netlifycms.org/docs/widgets/#relation"&gt;relation widget&lt;/a&gt;. This allows you to link two seperate entities. If you are familiar with SQL it's a bit like a join. In this blog I'll link my &lt;code&gt;blogs&lt;/code&gt; collection with my &lt;code&gt;authors&lt;/code&gt; collection.&lt;/p&gt;

&lt;p&gt;The configuration is rather simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ./static/admin/config.yml&lt;/span&gt;
&lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blog"&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Blog"&lt;/span&gt;
    &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/pages/blog"&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Template&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Key"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;templateKey"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hidden"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blog-post"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Body"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;markdown"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Author"&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author"&lt;/span&gt;
        &lt;span class="na"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;relation&lt;/span&gt;
        &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author"&lt;/span&gt;
        &lt;span class="na"&gt;displayFields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;searchFields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;valueField&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;
     &lt;span class="c1"&gt;# other fields omitted for brevity&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author"&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authors"&lt;/span&gt;
    &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/authors"&lt;/span&gt;
    &lt;span class="na"&gt;identifier_field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Template&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Key"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;templateKey"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hidden"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Job&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;job_title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Profile&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;picture"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;profile_picture"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;allow_multiple&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
     &lt;span class="c1"&gt;# other fields omitted for brevity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everywhere you query your blogs using GraphQL in Gatsby you will now receive a field with the &lt;code&gt;author&lt;/code&gt;'s name. But what if you do not only need the name, but also want to display the author's job title and profile picture? Well... that doesn't work out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieving all fields of a &lt;code&gt;relation&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;On my blog pages I want to show all fields relating to its author, so I had to work around this limitation of Netlify CMS. In this section I'll show two approaches to make this work. If you know a better way, let me know in the comments!&lt;/p&gt;

&lt;h3&gt;
  
  
  Populating browser side
&lt;/h3&gt;

&lt;p&gt;The first, more naive route I took was to enhance the graphql query on each page where I also retrieved blogs, by quering the &lt;code&gt;allMarkdownRemark&lt;/code&gt; collection again (under the alias &lt;code&gt;authors&lt;/code&gt;) to retrieve all authors.&lt;/p&gt;

&lt;p&gt;A shortened version of that query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BlogPost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;allMarkdownRemark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;frontmatter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="c"&gt;# other fields omitted for brevity&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;allMarkdownRemark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;templateKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;}}})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;frontmatter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;job_title&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;profile_picture&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that all the authors are available you can retrieve the author of the blog in the browser. This could be done during render of your component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/templates/blog.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;markdownRemark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// ❗ Not very efficient: providing all authors then looking one up. &lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthorComponent&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This author retrieval could be optimized by utilizing a &lt;code&gt;[useMemo](https://reactjs.org/docs/hooks-reference.html#usememo)&lt;/code&gt; hook. &lt;br&gt;
However, this approach has still several downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;this goes against the GraphQL principle of querying only what you need&lt;/li&gt;
&lt;li&gt;all the unnecessary data is added to your javascript chunks, bloating your file size&lt;/li&gt;
&lt;li&gt;if you have many authors or finding even more relations in the browser, this will cause a performance penalty sooner or later.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Populating server side (using &lt;code&gt;gatsby-node&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;A better solution would be to enrich the blog posts during server-side generation. This would increase build time slightly on during page generation at the advantage of better performance on the browser side. A trade-off I'm happy to make!&lt;/p&gt;

&lt;p&gt;The gist of this approach is to understand that within Netlify CMS all content types are saved in Markdown. As I explained in a &lt;a href="https://dev.to/patricksevat/a-beginners-guide-to-understanding-your-gatsby-starter-template-4m6d-temp-slug-3341372"&gt;previous blog&lt;/a&gt; page creation is done is done in &lt;code&gt;gatsby-node.js&lt;/code&gt;. In that file, &lt;code&gt;allMarkdownRemark&lt;/code&gt; content is retrieved. The emphasis here is on &lt;em&gt;all content&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To create blogs we filter on a certain &lt;code&gt;templateKey&lt;/code&gt;, but that also means that with the right query we can make all authors available during page creation time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    {
      allMarkdownRemark(limit: 1000) {
        edges {
          node {
            # used to retrieve individual blog data in page queries
            id
            frontmatter {
              templateKey
              # the author fields in Blog that represents a relation
              author
              # field on the Author content type
              name 
              # field on the Author content type
              job_title
              # field on the Author content type
              profile_picture 
            }
          }
        }
      }
    }
  `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allMarkdownRemark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blogs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;templateKey&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog-post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;templateKey&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;author&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
      &lt;span class="nx"&gt;createPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/templates/blog.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="c1"&gt;// additional data can be passed via context&lt;/span&gt;
        &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;context&lt;/code&gt; field can be used during page creation &lt;a href="https://www.gatsbyjs.org/docs/recipes/pages-layouts/#directions-3"&gt;to provide extra data&lt;/a&gt; to the render props of the React Component that is used to generate the page. We can now refactor our Blog template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/templates/blog.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;markdownRemark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthorComponent&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! If you know a different or more efficient way of retrieving full relations, drop your knowledge in the comments!&lt;/p&gt;




&lt;p&gt;This blog is part of a series on how I migrated away from a self-hosted Drupal blog to a modern JAM stack with Gatsby and Netlify CMS.&lt;/p&gt;

</description>
      <category>netlifycms</category>
      <category>gatsby</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>A beginners guide to understanding your Gatsby starter template</title>
      <dc:creator>Patrick Sevat</dc:creator>
      <pubDate>Thu, 27 Feb 2020 21:41:51 +0000</pubDate>
      <link>https://dev.to/patricksevat/a-beginners-guide-to-understanding-your-gatsby-starter-template-4f1f</link>
      <guid>https://dev.to/patricksevat/a-beginners-guide-to-understanding-your-gatsby-starter-template-4f1f</guid>
      <description>&lt;p&gt;&lt;em&gt;Illustration by Katerina Limpitsouni &lt;a href="//undraw.co"&gt;undraw.co/illustrations&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently I started migrating an older Drupal based blog to a combination of Gatsby and Netlify CMS (&lt;a href="https://github.com/patricksevat/de-europeanen-jamstack"&gt;source&lt;/a&gt;), both of which I had no prior experience with. In this blog series I'll talk you through the experiences, hurdles and solutions. In part 3 I'll explain what's happening in your Gatsby starter template. Although a starter kit is a good place to start, for those with no prior knowledge of Gatsby and GraphQL it might look like magic 🧙‍♂️. So let's try and demystify!&lt;/p&gt;

&lt;p&gt;For my project I used the &lt;a href="https://templates.netlify.com/template/gatsby-blog-with-netlify-cms/"&gt;Gatsby + Netlify CMS starter&lt;/a&gt;, although much of the information will also be applicable to other starters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Page creation
&lt;/h2&gt;

&lt;p&gt;The most basic entity for Gatsby is a page. If you were to create a "vanilla Gatsby" project, every &lt;code&gt;.js&lt;/code&gt; file with a React Component within the &lt;code&gt;src/pages&lt;/code&gt; directory would be compiled to an HTML page. This still holds true for your starter project, even though you might find an &lt;code&gt;index.md&lt;/code&gt;, &lt;code&gt;index.yml&lt;/code&gt; or &lt;code&gt;index.json&lt;/code&gt; file in your template.&lt;/p&gt;

&lt;p&gt;So, how does the conversion from markdown file to JavaScript happen?&lt;/p&gt;

&lt;p&gt;This process happens in the &lt;code&gt;gatsby.node.js&lt;/code&gt; file. Let's take a closer look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// gatsby.node.js (parts omitted for brevity)&lt;/span&gt;

&lt;span class="c1"&gt;// 1️⃣&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;

  &lt;span class="c1"&gt;// 2️⃣&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    # you'll find a biggg GraphQL query here
  `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ℹ your datastructure might differ&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allMarkdownRemark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
      &lt;span class="c1"&gt;// 3️⃣&lt;/span&gt;
      &lt;span class="nx"&gt;createPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`src/templates/page.js`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="c1"&gt;// additional data can be passed via context&lt;/span&gt;
        &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we see is that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;the &lt;code&gt;createPages()&lt;/code&gt; method of the &lt;a href="https://www.gatsbyjs.org/docs/node-apis/#createPages"&gt;Gatsby Node API &lt;/a&gt; is being utilized to programmatically create new pages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This method accepts an Promise. The Promise returned here is a GraphQL query. How and where GraphQL gets its data will be explained in the next section. For now we will assume that this query returns some raw data that we have saved in our markdown/yaml/json file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For each block of raw page data we create a new page. This is done by the &lt;a href="https://www.gatsbyjs.org/docs/actions/#createPage"&gt;createPageAction&lt;/a&gt;. This causes Gatsby to create a new page based on the component defined. In our case the component is a JavaScript template file which contains a React Component. As we already know, Gatsby is capable of converting React Components into (static) pages with HTML, CSS and JS.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Data sourcing in Gatsby
&lt;/h2&gt;

&lt;p&gt;Now that we have high-level knowledge on how pages are created, let's do an inspection on how the page data is created.&lt;/p&gt;

&lt;p&gt;We know the source of the page data: &lt;code&gt;index.md&lt;/code&gt;, we also know the output: it is part of a GraphQL response. The big question is how is this tranformation done?&lt;/p&gt;

&lt;p&gt;The answer can be found in Gatsby's plugins. The plugins are defined in &lt;code&gt;gatsby.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;siteMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... metadata&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// several plugins omitted for brevity&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gatsby-source-filesystem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/src/pages`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gatsby-transformer-remark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gatsby-plugin-netlify-cms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;modulePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/src/cms/cms.js`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gatsby-plugin-netlify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// make sure to keep it last in the array&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first plugin that does its work in Gatsby is very often the &lt;code&gt;gatsby-source-filesystem&lt;/code&gt;. It reads the files in a certain directory, in this case &lt;code&gt;src/pages/&lt;/code&gt; and reads &lt;strong&gt;all&lt;/strong&gt; files recursively (though there is an ignore option). The &lt;code&gt;source-filesystem&lt;/code&gt; plugin then creates an intermediate entity, called a &lt;code&gt;File&lt;/code&gt; which can be picked up by other plugins for further processing.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;File&lt;/code&gt; contains metadata on the extension and the path to find this file.&lt;/p&gt;

&lt;p&gt;After the &lt;code&gt;File&lt;/code&gt; is created, subsequent plugins can process it further. In our example, &lt;code&gt;gatsby-transformer-remark&lt;/code&gt; filters all &lt;code&gt;File&lt;/code&gt;s with extensions &lt;code&gt;md&lt;/code&gt; and &lt;br&gt;
 &lt;code&gt;markdown&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that the &lt;code&gt;transformer-remark&lt;/code&gt; has knowledge about the file's location, it can read the file, parse it and make the processed result available under a new &lt;a href="https://graphql.org/learn/schema/#object-types-and-fields"&gt;GraphQL type&lt;/a&gt;: &lt;code&gt;MarkdownRemark&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We now know the process that happen under the hood in processing from file to data queryable via GraphQL.&lt;/p&gt;
&lt;h2&gt;
  
  
  A short intro to GraphQL in Gatsby
&lt;/h2&gt;

&lt;p&gt;Learning GraphQL is beyond the scope of this article and &lt;a href="https://www.howtographql.com/"&gt;worthy of a full recommended course&lt;/a&gt;. So I'll keep it short and to the bare minimum to get going.&lt;/p&gt;
&lt;h3&gt;
  
  
  Revisiting &lt;code&gt;gatsby.node.js&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In the previous section we learned that there is a new &lt;code&gt;MarkdownRemark&lt;/code&gt; type added to GraphQL. We can see this when we revisit the query we skipped earlier in &lt;code&gt;gatsby.node.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    {
      allMarkdownRemark(limit: 1000) {
        edges {
          node {
            id
            frontmatter {
              title
              body
            }
          }
        }
      }
    }
  `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// create pages&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now understand that &lt;code&gt;allMarkdownRemark&lt;/code&gt; will return an array of markdown page data. It will do so in this datastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;allMarkdownRemark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My awesome blog about Gatsby + Netlify CMS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lorem ipsum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Querying at the page level
&lt;/h3&gt;

&lt;p&gt;If you'd want to query a single page, you'd query by the id field.&lt;br&gt;
The &lt;code&gt;id&lt;/code&gt; field is added to the page context (scroll up to &lt;code&gt;createPage&lt;/code&gt;) and used in the query defined in the template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/templates/page.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="s2"&gt;`
  query PageByID($id: String!) {
    markdownRemark(id: { eq: $id }) {
      html
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        title
      }
    }
  }
`&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;markdownRemark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;markdownRemark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frontmatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;markdownRemark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A couple things stand out in this snippet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is an exported &lt;code&gt;const&lt;/code&gt; called &lt;code&gt;query&lt;/code&gt;. This constant will be picked up by Gatsby, the query will be executed and passed as &lt;code&gt;props.data&lt;/code&gt; to the component.&lt;/li&gt;
&lt;li&gt;We see the &lt;code&gt;$id&lt;/code&gt; parameter being injected into the query. As stated before, any parameter is being looked up via the page context. The page context is set during page creation in &lt;code&gt;gatsby-node.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Rather than using &lt;code&gt;allMarkdownRemark&lt;/code&gt; which returns an array we use &lt;code&gt;markdownRemark&lt;/code&gt; as Query type here. This makes sure a single object is returned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it, you know all you need to know to go play around with pages and queries! Try to create some new content, content types or fiddle around with queries such as sorting by date.&lt;/p&gt;

&lt;p&gt;🧪 Tip: if you are going to play around with the GraphQL be sure to turn on the &lt;a href="https://www.gatsbyjs.org/docs/using-graphql-playground/"&gt;GraphQL Playground&lt;/a&gt; by changing your npm develop script to &lt;code&gt;"develop" : "GATSBY_GRAPHQL_IDE=playground gatsby develop"&lt;/code&gt;. This allows you to use the more intuitive &lt;a href="https://github.com/prisma-labs/graphql-playground"&gt;Prisma GraphQL Playground&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;This blog is part of a series on how I migrated away from a self-hosted Drupal blog to a modern JAM stack with Gatsby and Netlify CMS.&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>A beginners guide to setting up Netlify CMS</title>
      <dc:creator>Patrick Sevat</dc:creator>
      <pubDate>Thu, 27 Feb 2020 21:40:54 +0000</pubDate>
      <link>https://dev.to/patricksevat/a-beginners-guide-to-setting-up-netlify-cms-3f1h</link>
      <guid>https://dev.to/patricksevat/a-beginners-guide-to-setting-up-netlify-cms-3f1h</guid>
      <description>&lt;p&gt;&lt;em&gt;Illustration by Katerina Limpitsouni &lt;a href="//undraw.co"&gt;undraw.co/illustrations&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently I started migrating an older Drupal based blog to a combination of Gatsby and Netlify CMS (&lt;a href="https://github.com/patricksevat/de-europeanen-jamstack" rel="noopener noreferrer"&gt;source&lt;/a&gt;), both of which I had no prior experience with. In this blog series I'll talk you through the experiences, hurdles and solutions. In part 2 I'll give some pointers how to set up Netlify CMS to your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Netlify CMS is a headless Content Management System. Think Wordpress, but only the admin portal. Another key difference is that it does not use any database.&lt;/p&gt;

&lt;p&gt;Wait... what?! Yes, you read it right. Netlify CMS is part of the &lt;a href="https://headlesscms.org/about#type-git-based--api-driven" rel="noopener noreferrer"&gt;Git-based headless CMS group&lt;/a&gt;. It works by committing &lt;a href="https://www.markdownguide.org/" rel="noopener noreferrer"&gt;markdown files&lt;/a&gt; to your repo. Alternatively you could also store your content as &lt;a href="https://www.netlifycms.org/docs/configuration-options/#extension-and-format" rel="noopener noreferrer"&gt;JSON, yml or toml&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using Git as your content database &lt;a href="https://headlesscms.org/about#type-git-based--api-driven" rel="noopener noreferrer"&gt;comes with some downsides&lt;/a&gt; and you might think that content editing is reserved to persons with access to the Git repo, but fortunately, Netlify CMS provides a good way to manage your content for non-devs which I will discuss in the next section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6vdpo5x9ba6pm2bt8rbo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6vdpo5x9ba6pm2bt8rbo.jpg" alt="An impression of the admin interface of Netlify CMS"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;An impression of the admin interface of Netlify CMS&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To create some content you can login to the admin interface (defaults to &lt;code&gt;/admin&lt;/code&gt;) and start editing in the User Interface. When you click on Publish, Netlify CMS will create a commit on the master branch of your repo, adding or modifying the Markdown file.&lt;/p&gt;

&lt;p&gt;The commit on master is picked up by Netlify (the platform, not the CMS) which is registered as a Github App. Netlify schedules a new production build and as soon as that is finished, your app will be updated with new content!&lt;/p&gt;

&lt;p&gt;❗ This means that before you start working on your site, you have to remember to pull the latest data from master.&lt;/p&gt;

&lt;p&gt;If you are confused what the difference between Netlify and Netlify CMS is, I recommend reading &lt;a href="https://www.netlifycms.org/docs/intro/#netlify-cms-vs-netlify" rel="noopener noreferrer"&gt;this link&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;The first thing I wanted to know when considering Netlify CMS was the authentication part. For my use case the content editors should not have to create a Github account.&lt;/p&gt;

&lt;p&gt;Fortunately, with &lt;a href="https://www.netlifycms.org/docs/authentication-backends/#git-gateway-with-netlify-identity" rel="noopener noreferrer"&gt;Identity&lt;/a&gt; you can invite editors to the admin interface. They can sign up using an email/password combination or by linking an external OAuth provider (currently Google, Github, Bitbucket and GitLab).&lt;/p&gt;
&lt;h2&gt;
  
  
  Customizing fields
&lt;/h2&gt;

&lt;p&gt;If you first open up the admin interface you'll be met with some basic options for creating and editing different content types. You can manage for individual pages (such as the home page or the about page) and for sets of pages such as blogs.&lt;/p&gt;

&lt;p&gt;You'll find that individual pages will have different fields in the CMS. A home page might have a field with highlighted blogs. That field will be missing on the about page. In other words, the fields of these pages are not uniform.&lt;/p&gt;

&lt;p&gt;Blogs on the other hand have the same set of fields, although some may be optional. This distinction is the difference between &lt;a href="https://www.netlifycms.org/docs/collection-types/" rel="noopener noreferrer"&gt;Collection types&lt;/a&gt;. Individual pages belong to the &lt;code&gt;file&lt;/code&gt; content type, whereas sets of similar content belong to the &lt;code&gt;folder&lt;/code&gt; content type.&lt;/p&gt;

&lt;p&gt;You can have multiple of either &lt;code&gt;collection types&lt;/code&gt;. You could have a &lt;code&gt;file&lt;/code&gt; for the home page and another for the contact page. On the other hand you can have seperate &lt;code&gt;folder&lt;/code&gt; content types for blogs and authors.&lt;/p&gt;

&lt;p&gt;The configuration for these content types can be found in the &lt;code&gt;static/admin/config.yml&lt;/code&gt; file. In basic form the content types would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# other CMS configuration&lt;/span&gt;

&lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blog"&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Blog"&lt;/span&gt;
    &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/pages/blog"&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Body"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;markdown"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author"&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authors"&lt;/span&gt;
    &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/authors"&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Profile&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;picture"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;profile_picture"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;allow_multiple&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pages"&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pages"&lt;/span&gt;
    &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/pages/index.md"&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home"&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index"&lt;/span&gt;
        &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Home&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/pages/contact/index.md"&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Contact"&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contact"&lt;/span&gt;
        &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Contact&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding new fields is rather easy. You just add a new line with the proper configuration, commit and push the changes and it will show up in the admin panel.&lt;/p&gt;

&lt;p&gt;There are many field types to choose from. In Netlify CMS field types are defined by the &lt;code&gt;widget&lt;/code&gt; property. You got various options including textareas, file uploads and select fields. All available widgets can be viewed &lt;a href="https://www.netlifycms.org/docs/widgets/#common-widget-options" rel="noopener noreferrer"&gt;here&lt;/a&gt;. If the widget you would like is not present, there's even the possibility &lt;a href="https://www.netlifycms.org/docs/custom-widgets/" rel="noopener noreferrer"&gt;to create a custom widget&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can now set up your CMS as you see fit. Don't worry if you need or want to iterate on it. You can add fields later on as well. If you decide to rename them, you'll have to edit the markdown files to update the fields there as well.&lt;/p&gt;

&lt;p&gt;When inspecting the Markdown file you might encounter some syntax that you have not seen before. It looks like the Markdown is enhanced with some metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;awesome&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;blog"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="gs"&gt;**The body of the blog post**&lt;/span&gt;

Typing &lt;span class="sb"&gt;`Markdown`&lt;/span&gt; as you &lt;span class="ge"&gt;*know it*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This metadata format is known as &lt;a href="https://jekyllrb.com/docs/front-matter/" rel="noopener noreferrer"&gt;frontmatter&lt;/a&gt;. Essentially, it is &lt;code&gt;yaml&lt;/code&gt; syntax. All fields are saved as frontmatter data, except the &lt;code&gt;body&lt;/code&gt; field if the body &lt;code&gt;field&lt;/code&gt; uses the markdown widget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Previewing your content in the admin interface
&lt;/h2&gt;

&lt;p&gt;Even though you added some new fields to your content types and you filled them in, they will not show up immediately in the preview pane. To get that working you must register a &lt;a href="https://www.netlifycms.org/docs/customization/#registerpreviewtemplate" rel="noopener noreferrer"&gt;&lt;code&gt;previewTemplate&lt;/code&gt;&lt;/a&gt;. If you use any of the Netlify CMS starters the template is already created for you. All you need to do is retrieve the new data and update your rendered component(s) with this new data.&lt;/p&gt;

&lt;h2&gt;
  
  
  A local CMS
&lt;/h2&gt;

&lt;p&gt;One last tip: if you are developing locally you might want to test with some mocked data (looking at you Foo Blog 🙄) or you might be working on the preview templates mentioned in the previous section.&lt;/p&gt;

&lt;p&gt;Either way, it is possible to run a local version of the CMS using a &lt;a href="https://www.netlifycms.org/docs/beta-features#working-with-a-local-git-repository" rel="noopener noreferrer"&gt;beta feature that sets up a proxy&lt;/a&gt;. This will make sure that any content you create is not pushed to the repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  My final configuration
&lt;/h2&gt;

&lt;p&gt;You can find the CMS configuration I ended up with in my &lt;a href="https://github.com/patricksevat/de-europeanen-jamstack/blob/master/static/admin/config.yml" rel="noopener noreferrer"&gt;Github repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You might notice that I created a relationship between the Blog content type and the Author content type. The how and why of that relationship is the subject of the next blog!&lt;/p&gt;




&lt;p&gt;This blog is part of a series on how I migrated away from a self-hosted Drupal blog to a modern JAM stack with Gatsby and Netlify CMS.&lt;/p&gt;

</description>
      <category>netlifycms</category>
      <category>cms</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>How to choose your JAM stack</title>
      <dc:creator>Patrick Sevat</dc:creator>
      <pubDate>Thu, 27 Feb 2020 21:40:15 +0000</pubDate>
      <link>https://dev.to/patricksevat/how-to-choose-your-jam-stack-2bi1</link>
      <guid>https://dev.to/patricksevat/how-to-choose-your-jam-stack-2bi1</guid>
      <description>&lt;p&gt;&lt;em&gt;Illustration by Katerina Limpitsouni &lt;a href="//undraw.co"&gt;undraw.co/illustrations&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Over the last months I have migrated my Drupal and Wordpress websites to the &lt;a href="https://jamstack.org/"&gt;JAMstack (Javascript, API and Markup)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This blog series will focus on my first endeavour with Gastby and Netlify CMS, but this episode will also focus on my experience with other libraries and providers in the ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 reasons to migrate to the JAM stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No more server management! 
Upgrading LAMP stacks, maintaining Linux servers requires a lot of attention and knowledge and it is not my strong suit&lt;/li&gt;
&lt;li&gt;For small sites, you can host for free or very cheap using providers like Netlify, Vercel and others&lt;/li&gt;
&lt;li&gt;Learning new tools is FUN!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  JAM stack tool considerations
&lt;/h2&gt;

&lt;p&gt;You can find plenty of blogs or websites discussing the benefits and various tools available to you. To help you make a decision I'd recommend you consider these points. I'll share my personal findings for each point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Easy deploy
&lt;/h3&gt;

&lt;p&gt;I want to spend as little time configuring build/copy steps, exposing (public) folders and setting up SSL certificates as possible. &lt;strong&gt;I just want to define one npm script to execute and let the service do the rest&lt;/strong&gt;. There are a couple options here such as Heroku, Netlify and Vercel. Due to the availability of a complementary CMS, form submission handling and a good starter template, I ended up with Netlify. But also give a good look at Vercel!&lt;/p&gt;

&lt;h3&gt;
  
  
  CMS for non-tech users
&lt;/h3&gt;

&lt;p&gt;My users require a CMS with a admin interface which they can use. I also do not want to self-host my CMS elsewhere (headless CMS like Wordpress API). You can explore options on &lt;a href="https://headlesscms.org/"&gt;headlessCMS.org&lt;/a&gt;. Consider if you have a preference for a git-based or API-driven / SaaS CMS.&lt;/p&gt;

&lt;p&gt;Some good options: Contentful (SaaS &amp;amp; free tier), Netlify CMS (git-based, so free), Strapi (self-hosted)&lt;/p&gt;

&lt;h3&gt;
  
  
  Extendable CMS (custom fields)
&lt;/h3&gt;

&lt;p&gt;Make sure that your CMS supports extention of fields. Have you thought about comments, attachments, tags? Make sure your CMS supports adding these later on. Even better, your CMS should support creating new entities. For example I created an Author entitity so my users can place content on behalf of the actual authors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modern techniques
&lt;/h3&gt;

&lt;p&gt;New tools not only drive your learnings, but also bring benefits such as faster loading. See if you can utilize new optimizations such as GraphQL, webp image formats, serverless functions, CDN edge servers and variable fonts. Don't forget the advances on the developer experience as well. Tools such as dependabot, Cypress.io and TravisCI can make updating a breeze.&lt;/p&gt;

&lt;h3&gt;
  
  
  Big community / ecosystem
&lt;/h3&gt;

&lt;p&gt;Whenever choosing your JAM stack tools, you can make your life easier by &lt;strong&gt;choosing tools that are welcoming to newcomers&lt;/strong&gt;. The documentation of Gatsby can be daunting at first due to its sheer size, but it does an amazing job of gradually learning you everything you need to know. To make things even easier there are plenty of starter templates for you to pick from. Gatbsy also has an amazing ecosystem of plugins that provide you with all kinds of functionality.&lt;/p&gt;

&lt;p&gt;Another recommendation in the JavaScript space would be to consider &lt;a href="https://www.11ty.dev/"&gt;11ty&lt;/a&gt;, it is less mature than Gatsby but still has quite a community and &lt;a href="https://www.11ty.dev/docs/plugins/"&gt;plugins&lt;/a&gt;. The same goes for Vuepress.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not too steep learning curve
&lt;/h3&gt;

&lt;p&gt;Although learning new technologies, frameworks and techniques is really fun, &lt;strong&gt;you should also keep it manageable&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Certain techniques are easier to pick up than others. Below an overview of the learning curves for all tech that I've encountered in my journey. When describing difficulty I assume decent knowledge of HTML/CSS/JS/JS frameworks, but no prior experience with particular technology&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Difficulty&lt;/th&gt;
&lt;th&gt;Comment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GraphQL&lt;/td&gt;
&lt;td&gt;8 / 10&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React&lt;/td&gt;
&lt;td&gt;6 / 10&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gatsby&lt;/td&gt;
&lt;td&gt;9 / 10&lt;/td&gt;
&lt;td&gt;Steep learning curve if stepping outside of templates, only try if comfortable with React or GraphQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Svelte&lt;/td&gt;
&lt;td&gt;5 / 10&lt;/td&gt;
&lt;td&gt;Easy to pick up, ecosystem not fully mature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sapper&lt;/td&gt;
&lt;td&gt;5 / 10&lt;/td&gt;
&lt;td&gt;Same as Svelte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vue&lt;/td&gt;
&lt;td&gt;5 / 10&lt;/td&gt;
&lt;td&gt;Easy to pick up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vuepress&lt;/td&gt;
&lt;td&gt;7 / 10&lt;/td&gt;
&lt;td&gt;Takes a bit to understand once going outside of themes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  My experiences
&lt;/h2&gt;

&lt;h3&gt;
  
  
  CMS
&lt;/h3&gt;

&lt;p&gt;My biggest doubt when I embarked on this journey was that it might be difficult to find a CMS flexible enough to adjust to all the custom fields I needed, while at the same time be friendly to non technical editors.&lt;/p&gt;

&lt;p&gt;Netlify CMS proved to handle both. Fields are extremely extendable. Netlify CMS also supports custom content types. Adding relations between content types was a challenge though (more on that in a next post).&lt;/p&gt;

&lt;p&gt;Non tech users can create and edit content with the help of &lt;a href="https://docs.netlify.com/visitor-access/identity/registration-login/#external-provider-authentication"&gt;Identity&lt;/a&gt;. In the free tier that means your content editors can log-in using a Google account.&lt;/p&gt;

&lt;p&gt;To be honest, Netlify was the first CMS I considered, also due to the ease of deployment and availability of start templates. I'm pretty sure other CMS systems would also satifsy these requirements so let me know your experiences!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update, july 2020&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Even though I experimented with other site generators (see next section), I stuck with Netlify CMS as my content system!&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Site Generation (SSG)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Update, july 2020&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;This post originally only covered Gatsby, since then I've also tried other static site generators. This section is now updated.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Gatsby
&lt;/h4&gt;

&lt;p&gt;Gatsby is one of the big names within the JAMstack space.&lt;br&gt;
Strengths are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extensive, newbie-friendly documentation,&lt;/li&gt;
&lt;li&gt;newbie-friendly starter-kit and templates&lt;/li&gt;
&lt;li&gt;extensible plug-ins leading to a very lively and mature ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A potential downside is the learning curve if you are not already familiar with React or GraphQL. If you only need to learn either of those technologies, Gatsby is a great choice. If you are new to both React and GraphQL, choosing Gatsby might be too much to chew.&lt;/p&gt;

&lt;p&gt;Nonetheless, the docs do a great job at introducing all the concepts of both React and GraphQL. The rest of this blog series is also aimed at making Gatsby more understandable.&lt;/p&gt;

&lt;p&gt;The starter template I used is the &lt;a href="https://templates.netlify.com/template/gatsby-blog-with-netlify-cms/"&gt;Gatsby + Netlify CMS Starter&lt;/a&gt;. It proved a useful starting point, but my customizations were quick to follow as you can read in the rest of this series&lt;/p&gt;

&lt;h4&gt;
  
  
  Vuepress
&lt;/h4&gt;

&lt;p&gt;Vuepress is a static site generator within the Vue ecosystem.&lt;/p&gt;

&lt;p&gt;Strengths include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built upon Vue, which is very easy to learn and has an awesome ecosystem. For example, the &lt;a href="https://vuetifyjs.com/en/getting-started/quick-start/"&gt;Vuetify component library / design system&lt;/a&gt; is one of the best across all JS frameworks.&lt;/li&gt;
&lt;li&gt;Has many &lt;a href="https://github.com/vuepressjs/awesome-vuepress#themes"&gt;out-of-the-box themes&lt;/a&gt;. This resembles Wordpress theme experience, allowing you to get up and running very fast&lt;/li&gt;
&lt;li&gt;Is extensible enough to create your own theme (for example using Vuetify)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://github.com/vuepressjs/awesome-vuepress"&gt;ecosystem of Vuepress&lt;/a&gt; is less mature. For example, at the moment of writing there is no community-provided plugin for Netlify CMS relations (although I plan to write one, in the mean time check &lt;a href="https://github.com/patricksevat/de-zijderoute-vuepress/blob/master/docs/.vuepress/config.js#L26"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Sapper
&lt;/h4&gt;

&lt;p&gt;Sapper is a static site generator within the Svelte community.&lt;/p&gt;

&lt;p&gt;Strengths include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lightweight builds, interesting if performance is paramount for you&lt;/li&gt;
&lt;li&gt;Svelte is easy to learn&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ecosystem of Svelte is less mature than React (Gatsby) and Vuepress (Vue). I ran into this when looking for component libraries. &lt;a href="https://smeltejs.com/"&gt;Smelte&lt;/a&gt; is a perfect example: it is inspired by Vuetify, but less extensive. On the other hand it is way smaller and more performant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although I enjoyed experimenting with Sapper / Svelte, my personal preference is to lean towards something more batteries-included and take the performance penalty. But that is a trade-off every developer has themselve.&lt;/p&gt;




&lt;p&gt;This blog is part of a series on how I migrated away from a self-hosted Drupal blog to a modern JAM stack with Gatsby and Netlify CMS.&lt;/p&gt;

</description>
      <category>jamstack</category>
      <category>gatsby</category>
      <category>javascript</category>
      <category>cms</category>
    </item>
  </channel>
</rss>
