Editing terminology

An overview of the editing terminology in CloudCannon.

Some SSGs, like Jekyll, Hugo, Astro, and Eleventy, have strong opinions about how to organize the content in your repository. CloudCannon can run automatic processes on these SSGs and provide basic editing out-of-the-box. Next.js takes a less opinionated approach and allows you to decide where and how content is stored. This means that for Next.js, CloudCannon’s out-of-the-box options only include the source editor.

CloudCannon has a few concepts that make organizing data easier:

  • Data — These are standalone data files. This is ideal for site configuration or files that don’t need to be repeated. Data is edited by altering a single file.
  • Collections — A collection is a single folder of files with a repeated format. For example, a collection might include pages, blogs, staff members, or recipes that share a format. Collections are edited by altering any file or adding more files.

Fortunately, these terms align with the Next.js concepts of Data Fetching. More specifically:

Next.js can implement both of these calls for either Data or Collections. With a Collection, getStaticProps would pull in a single file, and getStaticPaths would pull in each file as a path. With Data, getStaticProps would pull in the whole file or an array item within it, and getStaticPaths would return nothing or each item in an array. We recommend you use Collections when implementing getStaticPaths.

CloudCannon supports a set of file formats for files in Data or Collections:

  • Structured data files: JSON, YAML, TOML, CSV, TSV
  • Markup files: HTML, Markdown, MDX
  • Combination files: HTML with front matter, Markdown with front matter, MDX with front matter

Front matter refers to a section at the top of a markup file that contains structured data. CloudCannon supports JSON, YAML, and TOML front matter. Markdown comes in many flavors, so be sure to check out configuring your Markdown engine.

If you need another supported format, get in touch with our support team, and we would be happy to look into it.

Updating Next.js to use file data#

Now that we have the terminology, it’s time to organize our content. Most of the Next.js documentation is weighted towards using a fetch for your getStaticProps and getStaticPaths.

CloudCannon edits content stored in its local file system. This means we need to alter the fetch to use an fs call instead of fetch. For example, in your page file, use the following:

copied
    export async function getStaticProps(context) {
    	const buffer = await fs.promises.readFile('config.json')
    	const data = JSON.parse(buffer.toString('utf8'))

    	return {
    		props: { data }
    	}
    }

Next.js documentation refers to this under Data Fetching: Reading files. The same process will need to happen for any calls to getStaticPaths.

It’s time to organize our content.

To get the most out of CloudCannon, it's best to make sure your content is separate from your templating and routing logic. We recommend storing your page content in files using one of the file formats listed above. Markdown with front matter is a great choice for storing content because the front matter can be easily edited in the Data Editor and the Markdown content Content Editor.

We recommend one content file per page, organized within a content/ folder.

With your content nicely organized, you'll then need to fetch this data into your page components. CloudCannon has an open-source package called Filer, which lets you easily pull data from your content files and collections.

For example, in a pages/blog/[slug].jsx (or equivalent) you could fetch your matching content/blog/slug.md file data like so:

copied
import Filer from '@cloudcannon/filer';
import DefaultLayout from '../../components/layouts/default';
import { formatDate } from '../../utility';

const filer = new Filer({ path: 'content' });

export default function Post({ page }) {
  return (
    <DefaultLayout page={page}>
      <h1>{page.data.title}</h1>

      <ul>
        <li>{formatDate(page.data.date, 'string')}</li>
        <li>{page.data.author}</li>
      </ul>

      <img
        src={page.data.image}
        alt={page.data.image_alt}
      />

      <article dangerouslySetInnerHTML={{ __html: page.content_html }}></article>
    </DefaultLayout>
  );
}

export async function getStaticPaths() {
  const postSlugs = await filer.listItemSlugs('posts');
  const paths = postSlugs.map((slug) => ({ params: { slug } }));

  return {
    paths,
    fallback: false,
  };
}

export async function getStaticProps({ params }) {
  const post = await filer.getItem(`${params.slug}.md`, {
    folder: 'posts',
  });

  return {
    props: {
      page: JSON.parse(JSON.stringify(post)),
    },
  };
}

For more usage examples, we recommend taking a look at our Sendit Next.js template.

Open in a new tab