This guide will walk through the steps required to get your Next.js site built, editable and live on CloudCannon.
Next.js is one of the most popular React-based SSGs. CloudCannon makes it easy to store your content in your Git Repository and have non-developers update it. Better yet, the editing is all done in context with minimal configuration.
The first step for connecting any site is to get your file system linked to CloudCannon. You’ll need to clone your files to disk before anything else — this is the same process as if you were working locally in your dev environment.
CloudCannon offers multiple ways of connecting your files:
It is highly recommended that you use the Git syncing option. This allows your updates on CloudCannon to push back to your repo. Your editors can then contribute to the Software Development Life Cycle (SDLC). This step is configured through an OAuth connection with the relevant partner. Check out the Connecting Your First Site guide for more information on this step.
The second step for any new site is to get a successful build. This also mirrors your local development environment. On CloudCannon, select Next.js as your SSG — this will set up the defaults for Next.js.
The important things to configure here are:
npm install
. This is run as a line of bash and can be anything you want. A common change here is to use yarn install
instead.npm run build
. This is similar to the install command. An example of this build is next build && next export
Ideally your build will work the first time. If this is the case, you can move to the next step. If your build did not work the first time, be sure to read the build output — this will let you know what didn’t work.
You may find you need extra commands to complete your build. A common example is using postcss; for this you can use build hooks, adding specially named bash scripts that run at different steps in the build. For more information, see the build hook documentation.
Once your site is built, you will see a big green tick on the screen. At this point, your site’s files are synced and a successful build has been run. This means you have unlocked a newly generated preview URL. In the top left corner of the screen under your site name, there is a blue link, styled adjective-noun.cloudvent.net. Clicking this link will open a new tab to the hosted site on CloudCannon. It is a helpful step at this point to check that the site looks correct. If it does, you can move on to editor configuration. If you need to update your build, this can be done in your settings.
If you find the site returned a 404 on the index, you might have set the wrong output path. This can be a sign that no files were present in the configured folder.
Some SSGs, like Jekyll, Hugo, and Eleventy, have strong opinions about how to organize the content in your repo. 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 out of the box only has the source editor available.
CloudCannon has a few concepts that make organizing data easier:
Fortunately, these terms align with the Next.js concepts of Data Fetching. More specifically:
Next.js can implement both of these calls with 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, andgetStaticPaths
would return nothing or each item in an array. It is recommended that Collections are used when implementing getStaticPaths
.
CloudCannon does not support Server-side Rendering as our build and hosting environments are decoupled.
CloudCannon supports a set of file formats for files in Data or Collections:
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 flavours, so be sure to checkout configuring your Markdown engine.
If you need another supported format, please contact support and we would be happy to look into it.
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. For example:
export async function getStaticProps(context) {
const res = await fetch(`https://...`)
const data = await res.json()
return {
props: { data }
}
}
CloudCannon edits content stored in the local file system. This means we need to change the fetch to use an fs
call instead. For example:
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. For our templates, we have created a new helper library to allow for combination markdown and front matter collections:
import { readdir, readFile } from 'fs/promises';
import path from 'path';
import matter from 'gray-matter';
import MarkdownIt from 'markdown-it';
import next from 'next';
const md = new MarkdownIt({ html: true });
const collectionsDirectory = path.join(process.cwd(), 'content');
export async function getCollectionSlugs(collection) {
const fileNames = await readdir(path.join(collectionsDirectory, collection));
return fileNames.map((fileName) => ({
params: {
slug: path.basename(fileName, path.extname(fileName))
}
}));
}
export async function getCollection(collection, options = {}) {
const fileNames = await readdir(path.join(collectionsDirectory, collection));
const collectionItems = await Promise.all(await fileNames.reduce(async (memo, fileName) => {
const slug = path.basename(fileName, path.extname(fileName));
if (!slug.startsWith('_')) {
const item = await getCollectionItem(collection, slug, options);
return [...await memo, item];
}
return memo;
}, []));
if (options.sortKey) {
return collectionItems.sort((a, b) => {
if (a[options.sortKey] === b[options.sortKey]) {
return 0;
}
return a[options.sortKey] > b[options.sortKey] ? -1 : 1;
});
}
return collectionItems;
}
export async function getCollectionItem(collection, slug, options = {}) {
const fullPath = path.join(collectionsDirectory, collection, `${slug}.md`);
const fileContents = await readFile(fullPath, 'utf8');
const parsed = matter(fileContents);
const contentHtml = md.render(parsed.content);
if (options.excerpt && !parsed.excerptHtml) {
parsed.data.excerptHtml = md.renderInline(parsed.content.split('\n').slice(1, 2).join(' '));
}
return {
...parsed.data,
slug,
contentHtml
};
}
export async function getNextCollectionItem(collection, slug, options = {}) {
const collectionItems = await getCollection(collection, options)
const index = collectionItems.map(function(e) { return e.slug; }).indexOf(slug);
return collectionItems[index+1];
}
This can be used in the following way:
import ClientLayout from '../../components/layouts/client';
import { getCollectionSlugs, getCollectionItem } from '../../lib/collections';
export default function Post({ page, portfolio }) {
return (
<ClientLayout page={page} portfolio={portfolio}/>
);
}
export async function getStaticPaths() {
const slugs = await getCollectionSlugs('clients');
const ignored = {
_defaults: true
};
return {
paths: slugs.filter(({ params }) => !ignored[params.slug]),
fallback: false
};
}
export async function getStaticProps({ params }) {
const page = await getCollectionItem('clients', params.slug);
const portfolio = await getCollectionItem('pages', 'portfolio');
return {
props: {
page: JSON.parse(JSON.stringify(page)),
portfolio: JSON.parse(JSON.stringify(portfolio))
}
};
}
This library will be turned into an NPM package in the future, which will allow for simpler documentation here.
For more usage examples, we recommend taking a look at our Urban Next.js template.
With all of our content organized into files, we need to tell CloudCannon how to read our structure. For this we use the CloudCannon reader, an open-source NPM library used to integrate editing.
Check out the documentation and define your collections. Once you have pushed your changes to CloudCannon, a new build will make new interfaces available. You will be able to:
You will have numerous editing options available to you:
Visual editing on CloudCannon can be broken down into two separate parts:
The final result is something intuitive and easy to teach.
That’s it for the base Next.js setup. Your Data Editor will still be using the automatic configuration. If you find that some inputs could be improved, be sure to check out how to configure inputs.
Now that you are happy with the experience as a developer, you can share it with your team or clients. If you have an internal team, see team sharing. If you have a client, see client sharing. If you need more information, check out our SSO/SAML support.
If you experience any issues with your Next.js build, don’t hesitate to contact our support team.