Working with content

Let's take the concept of "configuration, not code" further. In this next step, we'll illustrate the ability to modify your site's content, and have those changes reflect globally across all pages in your site. These changes will illustrate the following abilities:

"Separation of content and presentation" is a foundational best practice in web content management. Grow maintains this best practice by allowing you to store all of your content (and structure) in the config files in the /content/ directory of your pod, and not mixing them up with your HTML in /views/.

Content anatomy

Here's the anatomy of a pod's file-based content management. This is where your site's content is defined and stored. Collections and documents are identified internally by their file paths within a pod, so you should generally avoid changing the internal file paths of content files.

Path What Description
/content/ Content directory Holds all content collections.
/content/<collection>/ Content collection Holds all files within a single collection.
/content/<collection>/_blueprint.yaml Blueprint Defines configuration for a collection of content, including things like: URL path format, content structure, view, and localization behavior.
/content/<collection>/<document>.md Document Holds a single content document (including all field values, a Markdown body, and any blueprint overrides).

Changing content and properties

Now we'll go through an exercise where we change a piece of content and some of its properties. Let's open up the files /content/pages/_blueprint.yaml and /content/pages/ This is the blueprint for content in the pages collection, and the file where the content and config for the team page is stored, respectively. Here's what they look like (with comments explaining each line).

# In /content/pages/_blueprint.yaml...

path: /{slug}/             # The URL path format.
view: /views/pages.html    # The template to use.
title: Pages               # Collection's title.

  path: /{locale}/{slug}/  # Localized URL path format.
# In /content/pages/

---                        # Beginning of YAML front matter.

[email protected]: The Team          # Canonical document title.
$titles:                   # Alternate document titles.
  [email protected]: Team               # "nav" document title.
$order: 3                  # Order in the collection's default list of docs.
$view: /views/team.html    # Document's template (overrides blueprint config).

# Custom field names and values.
icon: users
- [email protected]: Matthias Freddie
  [email protected]: CEO
  [email protected]: Tandemque obscurum capit est ultro caesique manuque sensurum.
- [email protected]: Caelius Ruh
  [email protected]: CTO
  [email protected]: Utinam adimuntque obscenas perpetuo, aquaticus pereunt simul inde.

---                        # End of YAML front matter (beginning of Markdown body).
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

How it works

To build your web site, Grow looks through all of the pod's collections and documents. From each piece of content's configuration, Grow derives both the URL to serve the page at and the template used to render the page. Again, no code determines URLs or templates – just configs – which means you can manage the entire architecture of your site by changing your site configs.

Built-ins and custom fields

Why do some fields start with $? Field names prefixed with $ in YAML front matter are built-in fields. All fields that do not have a $ are custom, user-defined fields. You can reference any field in templates using {{doc.}}. In the above example, you can reference the list of people by using {{doc.people}}.

Tagged fields

Why do some fields end with @? This indicates that they are tagged for message extraction. When you translate your web site, Grow walks all the configs and templates in your pod, searching for tagged strings. Grow extracts all the tagged strings into a message catalog, which can then be translated either by a translator or a translation provider. Ending a field name in @ indicates that you want that field value to be translated – that's all there is to it!


The ability to easily change URLs (both during and after the development phase of projects) is an important feature and also a good example of the "configuration, not code" philosophy. URLs are defined by a mix of definitions in podspec.yaml, a collection's _blueprint.yaml, and each individual content document.

Currently, the "The Team" page inherits its URL from its blueprint (/content/pages/_blueprint.yaml). Let's override the URL for this page. Add the following line to the top of the document's YAML front matter.

$path: /custom-team-path/

This causes the "The Team" page to be served at /custom-team-path/. You can verify this by opening http://localhost:8080/custom-team-path/ in your browser. Now, immediately everywhere, all links to the "The Team" page will be updated to point to the page's new URL.

Cascading configs

The above example changes the URL for just the "The Team" page, but what if you wanted to change the URLs for all pages in the /content/pages/ collection? You do that in the collection's blueprint (/content/pages/_blueprint.yaml). Basically, configs cascade from the document-level to the blueprint-level. Any configs present in a document's YAML front matter will override configs from the collection's blueprint.

Changing templates

Just like URL path formats, documents inherit templates from their blueprint too. If you have five pages in a "pages" collection, but need to override the template for just one of the pages (like we do for the "The Team" page), you can do that by specifying the $view field in the document's config.

Back Next: Localizing your site