Integrating Netlify CMS with Routify

Netlify CMS' logo with an arrow pointing to Routify's logo

I chose Routify for a project and found close to zero doc on CMS integrations. After a bit of tinkering with Strapi, I figured I didn’t want to spin up a heroku dyno just for the CMS of a jamstack site and decided to give Netlify CMS a try. The integration process was much smoother.

Repository to demo the final result of this tutorial: github.com/FelDev/routify-netlifycms

Live site of that demo repo: routify-netlifycms.netlify.app

Getting set up

(A lot of the basics are pretty much what you’d find in Netlify CMS’ docs, but here is a quick recap anyway until we get to the Routify specific stuff)

It’s quite easy to add Netlify CMS to an already existing Routify project. But, if you’re starting from scratch, you’ll probably want to get started with Routify’s starter repo.

npx @roxi/routify init

At this point, run npm run dev to make sure everything is working as expected.

You can already add the files that will be needed for your CMS. Under the root of your static folder, make an admin folder containing two files: index.html and config.yml.

static
 └ admin
   ├ index.html
   └ config.yml
<!-- /static/index.html -->
<!doctype html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Content Manager</title>
  <script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
</head>
  <body>
  <!-- Include the script that builds the page and powers Netlify CMS -->
  <script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script>
  <script>
    if (window.netlifyIdentity) {
      window.netlifyIdentity.on("init", user => {
        if (!user) {
          window.netlifyIdentity.on("login", () => {
            document.location.href = "/admin/";
          });
        }
      });
    }
  </script>
  </body>
</html>
# /static/config.yml
backend:
  name: git-gateway
  branch: master # Branch to update (optional; defaults to master)
media_folder: "static/images/uploads" # Media files will be stored in the repo under static/images/uploads
public_folder: "/images/uploads" # The src attribute for uploaded media will begin with /images/uploads

Then link your project to a GitHub repository (you could also use GitLab or Bitbucket, but I’ll stick to GitHub here.)

Link your GitHub project to a Netlify site; “New site from Git”.

Once your Netlify site is live, enable Identity. The CMS will use it for authentication, but also to write directly in your Git repository. So, head over to “Site settings” -> “Identity” -> “Services” and enable Git Gateway.

While you’re there, you’ll probably want to set Identity so that it is invite-only; otherwise anyone will have the possibility to sign up as an admin of your website! Go to “Site settings” -> “Identity” -> “Registration”. Then go back to the Identity tab and invite yourself.

That’s all there is to do in Netlify’s UI.

Accepting your invitation

To accept your invitation, you must have Netlify’s Identity Widget running on your site. Find /static/__index.html and add the following line of code in the <head>.

<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>

Now, when you click the invitation link you’ve sent yourself, you should see this popup:

Popup that reads 'Complete your signup' with a password prompt.

Quick note on this Identity widget… If you don’t plan to invite people as admins frequently, you could remove the widget from the head of your website except for /admin/index.html, where it’s needed to sign in. But then you’d need to add it again whenever you invite someone, or whenever someone forgets their password.

Working locally

You’ll want to work locally rather than have your every change triggering a new build on Netlify.

To do so, run npx netlify-cms-proxy-server and add local_backend: true to your /admin/config.yml file.

You should be able to login at http://localhost:5000/admin and see an error message about a missing property ‘collections’. Let’s create it!

Routify specific stuff

This is getting Routify specific because your collections property is where you define the location of the content files managed by Netlify CMS. You could do it in various ways, I’ll propose two options.

Again, the Netlify CMS docs on collections are pretty clear so I won’t go in detail. The focus here is where we store our files and how we use them.

1st option: a CMS folder for all your content

One way to organize your content is to create a CMS folder for all the content being managed by the CMS.

# /static/admin/config.yml
# (omitting the beginning of the file for brevity)
collections:
  - name: "homepage"
    label: "Homepage"
    extension: "md"
    folder: "src/pages/cms/homepage"
    create: false # Allow users to create new documents in this collection
    slug: "{{title}}"
    fields:
      - { name: "title", label: "Title (<h1>)", widget: "string" }
      - { name: "text", label: "text", widget: "markdown" }
  - name: "markdown"
    label: "markdown"
    extension: "md"
    folder: "src/pages/cms/markdown"
    create: false # Allow users to create new documents in this collection
    slug: "{{name}}"
    fields:
      - { name: "title", label: "Title (<h1>)", widget: "string" }
      - { name: "text", label: "text", widget: "markdown" }

src/pages/cms/ contains both a homepage folder and a markdown folder. With this setup, we can already write markdown files to our repo. yay! But how do you get markdown in your svelte files?

With marked and a custom component is how.

Getting marked is the usual npm i marked and the custom component goes like this:

<!-- /src/pages/_component/GetCMSData.svelte -->
<script context="module">
  export function getCMSData(layout, path) {
    return layout
      .children.filter(c => c.path === "/cms")[0]
      .children.filter(c => c.path === `/cms/${path}`)[0]
      .children[0].meta.frontmatter
  }
</script>

This bad boy is going to be used like this:

<!-- src/pages/homepage.svelte -->
<script>
  import { layout } from "@roxi/routify";
  import { getCMSData } from "./_components/GetCMSData.svelte"
  const cmsData = getCMSData($layout, "homepage")
</script>

<h1>{cmsData.title}</h1>

This already works perfectly for single lines of text, but not so much with markdown. This is where the marked package comes handy:

<!-- src/pages/markdown.svelte -->
<script>
  import { layout } from "@roxi/routify";
  import { getCMSData } from "./_components/GetCMSData.svelte"
  import marked from "marked";
  const cmsData = getCMSData($layout, "markdown")
</script>

<h1>{cmsData.title}</h1>
<section class="content">
  {@html marked(cmsData.text)}
</section>

Aaaand we’re pretty much done!

2nd option: a folder for each page with its content

The second option is just a small difference in content organization.

Rather than having a CMS folder with subfolders for each page with CMS-managed content, have folders for each page right under /pages which contain both their content and their .svelte files.

…easier to express with a visual tree. In the folder structure below, “homepage” and “markdown” use the first strategy and “team” uses the second one.

├── pages
│   ├── _components
│   │   ├── BottomNav.svelte
│   │   └── GetCMSData.svelte
│   ├── _fallback.svelte
│   ├── _layout.svelte
│   ├── cms
│   │   ├── homepage
│   │   │   └── content.md
│   │   └── markdown
│   │       └── markdown.md
│   ├── homepage.svelte
│   ├── index.svelte
│   ├── markdown.svelte
│   └── team
│       ├── _layout.svelte
│       ├── félix-paradis.md
│       ├── index.svelte
│       ├── jakob-rosenberg.md
│       ├── rich-harris.md
│       └── youuuu.md

Conclusion

This approach is based on nothing but my own experimentations; such is life on the bleeding edge.

I specify that because I’m not sure that this approach is the right approach and I’ll gladly point to better ideas should they arise. In other words, let me know if there’s a better way. There is one big problem with this current setup: no image optimization is happening. I have to trust that content editors will upload properly sized images…

But I hope this blog post will help the next person looking for “Routify CMS”, if only by letting them know that yes, you can use a CMS with Routify. I’ve done it. You’re not alone.

Cheers!


Pssst!

Sign up to my newsletter and get a free PDF about how to get people to sign up for your newsletters!

Powered by EmailOctopus