DRY Netlify CMS config with Manual Initialization
Tags: netlify, gatsby, javascript, jamstack
Published: 2021-04-03 19:36:37

In 2019 I absolutely fell in love with JAMStack and static websites. My love blossomed since I've encountered Gatsby. **For a lot of websites out there, there is simply no better way to do create a website.** Both this website and my company's - Clean Commit - are using Gatsby with Netlify CMS.

Static websites are steadily growing as a percent of projects we work on. **Having experience using Gatsby paired with Netlify CMS I found a couple tweaks that should optimize your development experience with CMS configuration.**

## Netlify CMS's YAML configuration can quickly become a mess

If you had the pleasure (or rather, displeasure) of working with fairly complex content in Netlify CMS you know the `config.yml` file can grow quickly. One of the projects I worked on was a website for [Dionysus - a company that specializes in organizing cultural events](https://dionysus.events). **This website contains 100s of events that are interconnected.** At one point events were separated into different content types. **Maintaining that CMS setup with YAML content quickly turns into a mess.**

### How to solve this issue? Manual Initialization!

Easy. **Instead of YAML, we can use JavaScript to configure Netlify CMS** with [Manual Initialization](https://www.netlifycms.org/docs/beta-features/#manual-initialization). Setting up manual initialization is easy.

To enable manual initialization you need to create a JS file with the configuration and let Netlify CMS know to use it.

```javascript
{
	resolve: 'gatsby-plugin-netlify-cms',
		options: {
			manualInit: true,
	    modulePath: `${__dirname}/src/cms/cms.js`,
	  },
	},
}
```

I strongly suggest keeping all configuration files within `src/cms` to keep them well organized.

To use Netlify CMS with manual initialization, you also need to set up the general CMS settings.

```javascript
import CMS from 'netlify-cms-app'
import pages from '@/cms/pages'
import posts from '@/cms/collections/posts'

window.CMS_MANUAL_INIT = true

CMS.init({
  config: {
    load_config_file: false,
    backend: {
      name: 'git-gateway',
      branch: 'master',
    },
    media_folder: '/static/img',
    public_folder: '/img',
    collections: [pages, posts],
  },
})
```

## Organizing posts and pages with Netlify CMS

When using manual initialization with Netlify CMS I divide settings into 3 groups - pages (for unique pages), collections, and partials. Sometimes we need to add additional groups like settings.

Setting up pages is mostly straight-forward. The main file I use to control pages is `src/cms/pages/index.js`.

```javascript
import home from '@/cms/pages/home'
import blog from '@/cms/pages/blog'
import about from '@/cms/pages/about'
import privacy from '@/cms/pages/privacy'

const pages = {
  name: 'pages',
  label: 'Pages',
  files: [home, about, blog, privacy],
}

export default pages
```

The main pages file is used to organize page order in the CMS and loading new pages.

```javascript
import seo from '@/cms/partials/seo'
import SmallHero from '@/cms/partials/sections/small-hero'

const page = {
  file: 'content/pages/home.md',
  label: 'Home',
  name: 'Home',
  fields: [
    {
      label: 'Layout',
      name: 'layout',
      widget: 'hidden',
      default: 'contact',
    },
    {
      label: 'Type',
      name: 'type',
      widget: 'hidden',
      default: 'page',
    },
    {
      label: 'Title',
      name: 'title',
      widget: 'string',
      default: '',
      required: false,
    },
    SmallHero,
    seo,
  ],
}

export default page
```

### Keep Netlify CMS configuration DRY with partials

Every page file contains configuration for individual fields and uses partials to provide fields that are used across different collections/pages. The example above shows one section that is reused called `SmallHero`. The second most common `partial` we use is the `seo` partial. This partial provides metadata information for each page and collection item.

```javascript
const seo = {
  label: 'SEO Settings',
  name: 'seo',
  widget: 'object',
  collapsed: true,
  fields: [
    {
      label: 'Title',
      name: 'title',
      widget: 'string',
      required: false,
    },
    {
      label: 'Meta Description',
      name: 'description',
      widget: 'text',
      required: false,
    },
    {
      label: 'Image',
      name: 'image',
      widget: 'image',
      required: true,
      default: '/img/shareable-default.jpg',
    },
  ],
}

export default seo
```

With partials, you don't have to edit multiple files to make changes across collections or pages.

## Complex data structures with Netlify CMS

I love ACF when working with WordPress. A lot of our projects since 2018 are made using Flexible Content from the Advanced Custom Fields plugin. It gives great flexibility for page creation for end users without the need for the developer's input. Headless CMSs started to adopt that feature, Prismic has Slices, Butter CMS has Components and Netlify CMS has lists.

For more on complex content solution read [this article on recreating Flexible Content field with Netlify CMS & Gatsby](https://mrkaluzny.com/blog/acfs-flexible-content-with-gatsby-netlify-cms/)

Lists support `types` params that can enable you to create flexible content fields. I wouldn't try that without manual initialization. That old YAML file would grow enormous quickly. Not to mention moving types across different pages.

```javascript
import seo from '@/cms/partials/seo'

import SmallHero from '@/cms/partials/sections/SmallHero'
import DarkSection from '@/cms/partials/sections/DarkSection'
import Perks from '@/cms/partials/sections/Perks'
import Pointers from '@/cms/partials/sections/Pointers'
import Testimonials from '@/cms/partials/sections/Testimonials'

const collection = {
  name: 'services',
  label: 'Services',
  editor: {
    preview: false,
  },
  description: 'Service content',
  folder: 'content/services',
  slug: '{{slug}}',
  create: true,
  fields: [
    {
      label: 'Type',
      name: 'type',
      widget: 'hidden',
      default: 'service',
    },
    {
      label: 'Layout',
      name: 'layout',
      widget: 'hidden',
      default: 'Service',
    },
    {
      label: 'Title',
      name: 'title',
      widget: 'string',
      required: true,
    },
    {
      label: 'Featured Image',
      name: 'thumbnail',
      widget: 'image',
      required: false,
    },
    {
      label: 'Sections',
      name: 'sections',
      widget: 'list',
      types: [SmallHero, DarkSection, Perks, Pointers, Testimonials],
    },
    seo,
  ],
}

export default collection
```

The example above showcases how I created service pages on Clean Commit's website.

Here's an example section - `SmallHero`

```javascript
const smallHero = {
  label: 'Small Hero',
  name: 'hero',
  widget: 'object',
  collapsed: false,
  fields: [
    {
      label: 'Title',
      name: 'title',
      widget: 'string',
      required: false,
    },
    {
      label: 'Header',
      name: 'header',
      widget: 'string',
      required: false,
    },
    {
      label: 'Content',
      name: 'content',
      widget: 'markdown',
      required: false,
    },
  ],
}

export default smallHero
```

Types with Manual Initialization is a perfect match for complex website content, making it possible to use Netlify CMS on both small and medium projects efficiently. Moving configuration between projects is also a breeze.

## Key takeaways for keeping Netlify CMS configuration DRY

When your project gets large, **use Manual Initialization instead of YAML configuration**, it's easier to maintain.

**Divide your Netlify CMS configuration into 3 base groups** - pages, collections, and partials. Fields shared across different entities should become partials.

**For flexibility in content creation combine manual initialization with Netlify CMS types using List widget.** - For more on this topic read [this article on recreating Flexible Content field with Netlify CMS & Gatsby](https://mrkaluzny.com/blog/acfs-flexible-content-with-gatsby-netlify-cms/)

For more helpful tips [check out the Tips & Tricks category on my blog](https://mrkaluzny.com/blog/category/tips-tricks/)