Content & Model Files

By default, all content is stored in the same Git repository as your code. This setup is known as a Git-based CMS, and is quite powerful when paired with a structured content model and a model-aware editor (learn more).

In this setup, model definitions are also stored alongside the content. Stackbit will load all models from the .stackbit/models/ directory in your project (this is customizable if you also want to load models from other packages).

Page Content

The theme is configured to look for page content under the path content/pages.

By default, the structure of files in that directory will match the URL structure of your website, except where defined otherwise for specific page models in stackbit.yaml. There's also automatic handling for files named index.md.

Looking inside files in content/pages, you'll notice that the Markdown files also have front matter in YAML format. The front matter is the part that's bounded by --- at the top of the file. It contains all content properties except the actual Markdown content of the page. Depending on their content type, some page files don't actually have Markdown content at all, but only front matter (learn more).

One property which you'll find in the front matter all pages is layout: this holds the model name associated with this content, or in other words: the type of the content. Look in .stackbit/models/ and you'd find that model's definition.

Non-Page Content

Besides the page content, there are also non-page content files under content/data. These include content objects holding the global configuration of the website and its styling, one of which is theme-style.json we've covered in the previous chapter.

How Content is Loaded

This is an optional reading on how Sourcebit is configured to actually load content files into proper content objects. Skip to the next chapter if you don't want to dive into this topic now.

We've mentioned in the previous page that Sourcebit loads all content right as Next.js starts running, using the project-specific configuration in sourcebit.js.

Sourcebit is a modular framework which is configured as a pipeline of plugins and transformation functions, executed in a specific order:

  • First, a source plugin is defined for reading the raw data from configured sources.
  • Then, any additional transformation functions are defined.
  • Finally, a target plugin it used to prepare the content in a format that fits the target framework. In our case, that's Next.js.

Here is an excerpt from sourcebit.js, edited for brevity. We'll only cover here the main elements at high level.

// ...

module.exports = {
  plugins: [
    {
      module: require('sourcebit-source-filesystem'),
      options: {
        watch: isDev,
        sources: [
          { name: 'pages', path: path.join(__dirname, 'content/pages') },
          { name: 'data', path: path.join(__dirname, 'content/data') }
        ]
      }
    },
    flattenMarkdownData(),
    {
      module: require('sourcebit-target-next'),
      options: {
        liveUpdate: isDev,

        // commonProps should be a function returning an object to merge
        // with the props of all pagse.
        commonProps: (data) => {
          const site = data.find((page) => page.__metadata.id === 'content/data/config.json')
          return { site }
        },

        // pages should be a function returning an array of objects, one for each page.
        // Each object should have the page URL in the path property,
        // plus any other props that should be passed to rendering components
        pages: (data) => {
          // ...build object per page
        }
      }
    }
  ]
}

Here's what's happening in this code:

The file is exporting an object, which should contain an array of plugins for Sourcebit to execute in order. Items can either be objects naming a specific module (a Sourcebit plugin) plus its specific options, or the name of any function that receives data and mutates it.

First, the source plugin is specified: in this case, the filesystem source. It is given the locations of content files, and set to watch for any changes to content files while the project runs in development mode and reload the data.

Then, a transformation function is called properly parse Markdown files, separating front matter from body.

Lastly, the target plugin is configured for use with Next.js:

  1. When content changes, the plugin will notify Next.js to refresh the page.
  2. Next.js requires the developer to implement getStaticPaths() and getStaticProps(). This plugin provides helper methods to make the implementation of these functions trivial, and we'll cover that in a moment. To provide that assistance, the plugin requires custom logic that's specific to this project. This logic is contained above within two functions: commonProps() and pages(), which this plugin will call when it gets run.

Armed with all this configuration, Sourcebit will read the content, transform it and make it ready for the Site Generator to consume.

On to the Fun Part!

After all the tedious work above - which was fortunately configured for you - here is the fun part where all the prep work finally pays off: turning content into actual pages.