refactor: rename website to docs

This commit is contained in:
Daniel Lautzenheiser
2022-12-06 09:49:54 -05:00
parent 08e1b60297
commit a5481aaece
177 changed files with 2 additions and 2 deletions

View File

@ -0,0 +1,36 @@
{
"title": "Help us build the CMS of the future",
"subtitle": "Get support, give support and find out what's new through the channels below.",
"sections": [
{
"title": "Support",
"links": [
{
"title": "Static CMS Discord",
"description": "Live community chat for all things Static CMS",
"url": "https://discord.gg/ZWJM9pBMjj"
},
{
"title": "Static CMS Community",
"description": "Ask and answer questions on GitHub discussions tab",
"url": "https://github.com/StaticJsCMS/static-cms/discussions"
},
{
"title": "GitHub Issues",
"description": "Report bugs, request features and comment on existing issues",
"url": "https://github.com/StaticJsCMS/static-cms/issues"
}
]
},
{
"title": "Development",
"links": [
{
"title": "GitHub Project",
"description": "Issues board on the Static CMS GitHub organization",
"url": "https://github.com/orgs/StaticJsCMS/projects/1"
}
]
}
]
}

30
docs/content/config.json Normal file
View File

@ -0,0 +1,30 @@
{
"base_url": "https://static-cms.netlify.app/",
"repo_url": "https://github.com/StaticJsCMS/static-cms",
"site_title": "Static CMS | Open Source Content Management System",
"site_description": "Open source content management for your Git workflow. Use Static CMS with any static site generator for a faster and more flexible web project.",
"site_image": "/static-cms-logo.svg",
"site_keywords": ["static", "cms", "staticcms", "netlify"],
"footer": {
"buttons": [
{
"text": "Twitter",
"url": "https://twitter.com/StaticJsCMS"
},
{
"text": "GitHub",
"url": "https://github.com/StaticJsCMS/static-cms"
}
],
"links": [
{
"text": "Distributed under MIT License",
"url": "https://github.com/StaticJsCMS/static-cms/blob/main/LICENSE"
},
{
"text": "Code of Conduct",
"url": "https://github.com/StaticJsCMS/static-cms/blob/main/CODE_OF_CONDUCT.md"
}
]
}
}

View File

@ -0,0 +1,271 @@
---
group: Intro
title: Bundling
weight: 5
---
This tutorial guides you through the steps for adding Static CMS via a package manager to a site that's built with a common [static site generator](https://www.staticgen.com/). If you want to start form a template, the [Next Template](docs/start-with-a-template) provides a great example of bundling in action.
## Installation
To get started you need to install Static CMS via a package manager and save it to your project:
```bash
// npm
npm install @staticcms/core
// yarn
yarn add @staticcms/core
```
Then import it (assuming your project has tooling for imports):
```js
import CMS from '@staticcms/core';
import config from './config';
// Initialize the CMS object
CMS.init({ config });
// Now the registry is available via the CMS object.
CMS.registerPreviewTemplate('my-template', MyTemplate);
```
**Note**: Wherever you initialize Static CMS (via `CMS.init()`), it takes over the current page. Make sure you only run the initialization code on your CMS page.
## Configuration
Configuration is different for every site, so we'll break it down into parts. Add all the code snippets in this section to your `admin/config.js` file (which is passed into the `CMS.init({ config })` call). Alternatively, you can use a yaml file (`admin/config.yml`) instead of a javascript file.
### Backend
We're using [Netlify](https://www.netlify.com) for our hosting and authentication in this tutorial, so backend configuration is fairly straightforward.
For GitHub and GitLab repositories, you can start your Static CMS `config` file with these lines:
<CodeTabs>
```js
backend: {
name: 'git-gateway',
branch: 'main' // Branch to update (optional; defaults to main)
},
```
```yaml
backend:
name: git-gateway
branch: main # Branch to update (optional; defaults to main)
```
</CodeTabs>
_(For Bitbucket repositories, use the [Bitbucket backend](/docs/bitbucket-backend) instructions instead.)_
The configuration above specifies your backend protocol and your publication branch. Git Gateway is an open source API that acts as a proxy between authenticated users of your site and your site repo. (We'll get to the details of that in the [Authentication section](#authentication) below.) If you leave out the `branch` declaration, it defaults to `main`.
### Media and Public Folders
Static CMS allows users to upload images directly within the editor. For this to work, the CMS needs to know where to save them. If you already have an `images` folder in your project, you could use its path, possibly creating an `uploads` sub-folder, for example:
<CodeTabs>
```js
media_folder: 'images/uploads', // Media files will be stored in the repo under images/uploads
```
```yaml
# This line should *not* be indented
media_folder: 'images/uploads' # Media files will be stored in the repo under images/uploads
```
</CodeTabs>
If you're creating a new folder for uploaded media, you'll need to know where your static site generator expects static files. You can refer to the paths outlined above in [App File Structure](#app-file-structure), and put your media folder in the same location where you put the `admin` folder.
Note that the`media_folder` file path is relative to the project root, so the example above would work for Jekyll, GitBook, or any other generator that stores static files at the project root. However, it would not work for Hugo, Hexo, Middleman or others that store static files in a subfolder. Here's an example that could work for a Hugo site:
<CodeTabs>
```js
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
```
```yaml
# These lines should *not* be indented
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
```
</CodeTabs>
The configuration above adds a new setting, `public_folder`. While `media_folder` specifies where uploaded files are saved in the repo, `public_folder` indicates where they are found in the published site. Image `src` attributes use this path, which is relative to the file where it's called. For this reason, we usually start the path at the site root, using the opening `/`.
_If `public_folder` is not set, Static CMS defaults to the same value as `media_folder`, adding an opening `/` if one is not included._
### Collections
Collections define the structure for the different content types on your static site. Since every site is different, the `collections` settings differ greatly from one site to the next.
Let's say your site has a blog, with the posts stored in `_posts/blog`, and files saved in a date-title format, like `1999-12-31-lets-party.md`. Each post begins with settings in yaml-formatted front matter, like so:
```yaml
---
layout: blog
title: "Let's Party"
date: 1999-12-31 11:59:59 -0800
thumbnail: '/images/prince.jpg'
rating: 5
---
This is the post body, where I write about our last chance to party before the Y2K bug destroys us all.
```
Given this example, our `collections` settings would look like this in your Static CMS `config` file:
<CodeTabs>
```js
collections: [
{
name: 'blog', // Used in routes, e.g., /admin/collections/blog
label: 'Blog', // Used in the UI
folder: '_posts/blog', // The path to the folder where the documents are stored
create: true, // Allow users to create new documents in this collection
slug: '{{year}}-{{month}}-{{day}}-{{slug}}', // Filename template, e.g., YYYY-MM-DD-title.md
fields: [ // The fields for each document, usually in front matter
{ label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' },
{ label: 'Title', name: 'title', widget: 'string' },
{ label: 'Publish Date', name: 'date', widget: 'datetime' },
{ label: 'Featured Image', name: 'thumbnail', widget: 'image' },
{ label: 'Rating (scale of 1-5)', name: 'rating', widget: 'number' },
{ label: 'Body', name: 'body', widget: 'markdown' },
],
},
],
```
```yaml
collections:
- name: 'blog' # Used in routes, e.g., /admin/collections/blog
label: 'Blog' # Used in the UI
folder: '_posts/blog' # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
slug: '{{year}}-{{month}}-{{day}}-{{slug}}' # Filename template, e.g., YYYY-MM-DD-title.md
fields: # The fields for each document, usually in front matter
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' }
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- { label: 'Featured Image', name: 'thumbnail', widget: 'image' }
- { label: 'Rating (scale of 1-5)', name: 'rating', widget: 'number' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
</CodeTabs>
Let's break that down:
| Field | Description |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | Post type identifier, used in routes. Must be unique. |
| label | What the admin UI calls the post type. |
| folder | Where files of this type are stored, relative to the repo root. |
| create | Set to `true` to allow users to create new files in this collection. |
| slug | Template for filenames. `{{ year }}`, `{{ month }}`, and `{{ day }}` pulls from the post's `date` field or save date. `{{ slug }}` is a url-safe version of the post's `title`. Default is simply `{{ slug }}`. |
| fields | Fields listed here are shown as fields in the content editor, then saved as front matter at the beginning of the document (except for `body`, which follows the front matter). |
As described above, the `widget` property specifies a built-in or custom UI widget for a given field. When a content editor enters a value into a widget, that value is saved in the document front matter as the value for the `name` specified for that field. A full listing of available widgets can be found in the [Widgets doc](/docs/widgets).
Based on this example, you can go through the post types in your site and add the appropriate settings to your Static CMS `config` file. Each post type should be listed as a separate node under the `collections` field. See the [Collections reference doc](/docs/collection-overview) for more configuration options.
### Filter
The entries for any collection can be filtered based on the value of a single field. The example collection below only shows post entries with the value `en` in the `language` field.
<CodeTabs>
```js
collections: [
{
name: 'posts',
label: 'Post',
folder: '_posts',
filter: {
field: 'language',
value: 'en',
},
fields: [
{
name: 'language',
label: 'Language',
},
],
},
],
```
```yaml
collections:
- name: posts
label: Post
folder: _posts
filter:
field: language
value: en
fields:
- name: language
label: Language
```
</CodeTabs>
## Authentication
Now that you have your Static CMS files in place and configured, all that's left is to enable authentication. We're using the [Netlify](https://www.netlify.com/) platform here because it's one of the quickest ways to get started, but you can learn about other authentication options in the [Backends](/docs/backends-overview) doc.
### Setup on Netlify
Netlify offers a built-in authentication service called Identity. In order to use it, connect your site repo with Netlify.
### Enable Identity and Git Gateway
Netlify's Identity and Git Gateway services allow you to manage CMS admin users for your site without requiring them to have an account with your Git host or commit access on your repo. From your site dashboard on Netlify:
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you want only invited users to access your CMS, but if you're just experimenting, you can leave it open for convenience.
3. If you'd like to allow one-click login with services like Google and GitHub, check the boxes next to the services you'd like to use, under **External providers**.
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This authenticates with your Git host and generates an API access token. In this case, we're leaving the **Roles** field blank, which means any logged in user may access the CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
### Add the Netlify Identity Widget
With the backend set to handle authentication, now you need a frontend interface to connect to it. The open source Netlify Identity Widget is a drop-in widget made for just this purpose. To include the widget in your site, add the following script tag in two places:
```html
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
```
Add this to the `<head>` of your CMS index page at `/admin/index.html`, as well as the `<head>` of your site's main index page. Depending on how your site generator is set up, this may mean you need to add it to the default template, or to a "partial" or "include" template. If you can find where the site stylesheet is linked, that's probably the right place. Alternatively, you can include the script in your site using Netlify's [Script Injection](https://www.netlify.com/docs/inject-analytics-snippets/) feature.
When a user logs in with the Netlify Identity widget, an access token directs to the site homepage. In order to complete the login and get back to the CMS, redirect the user back to the `/admin/` path. To do this, add the following script before the closing `body` tag of your site's main index page:
```html
<script>
if (window.netlifyIdentity) {
window.netlifyIdentity.on('init', user => {
if (!user) {
window.netlifyIdentity.on('login', () => {
document.location.href = '/admin/';
});
}
});
}
</script>
```
Note: This example script requires modern JavaScript and does not work on IE11. For legacy browser support, use function expressions (`function () {}`) in place of the arrow functions (`() => {}`), or use a transpiler such as [Babel](https://babeljs.io/).
## Accessing the CMS
Your site CMS is now fully configured and ready for login!
If you set your registration preference to "Invite only," invite yourself (and anyone else you choose) as a site user. To do this, select the **Identity** tab from your site dashboard, and then select the **Invite users** button. Invited users receive an email invitation with a confirmation link. Clicking the link will take you to your site with a login prompt.
If you left your site registration open, or for return visits after confirming an email invitation, access your site's CMS at `yoursite.com/admin/`.
**Note:** No matter where you access Static CMS — whether running locally, in a staging environment, or in your published site — it always fetches and commits files in your hosted repository (for example, on GitHub), on the branch you configured in your Static CMS `config` file. This means that content fetched in the admin UI matches the content in the repository, which may be different from your locally running site. It also means that content saved using the admin UI saves directly to the hosted repository, even if you're running the UI locally or in staging.
Happy posting!

View File

@ -0,0 +1,299 @@
---
group: Intro
title: CDN Hosting
weight: 4
---
This tutorial guides you through the steps for adding Static CMS via a public CDN to a site that's built with a common [static site generator](https://www.staticgen.com/), like Jekyll, Next, Hugo, Hexo, or Gatsby. Alternatively, you can [start from a template](/docs/start-with-a-template) or dive right into [configuration options](/docs/configuration-options).
## App File Structure
A static `admin` folder contains all Static CMS files, stored at the root of your published site. Where you store this folder in the source files depends on your static site generator. Here's the static file location for a few of the most popular static site generators:
| These generators | store static files in |
| ------------------------------------------------------- | --------------------- |
| Jekyll, GitBook | `/` (project root) |
| Hugo, Gatsby, Nuxt 2, Gridsome, Zola, Sapper, SvelteKit | `/static` |
| Next, Nuxt 3 | `/public` |
| Hexo, Middleman, Jigsaw | `/source` |
| Wyam | `/input` |
| Pelican | `/content` |
| Spike | `/views` |
| VuePress | `/.vuepress/public` |
| Elmstatic | `/_site` |
| 11ty | `/_site` |
| preact-cli | `/src/static` |
| Docusaurus | `/static` |
If your generator isn't listed here, you can check its documentation, or as a shortcut, look in your project for a `css` or `images` folder. The contents of folders like that are usually processed as static files, so it's likely you can store your `admin` folder next to those. (When you've found the location, feel free to add it to these docs by [filing a pull request](https://github.com/StaticJsCMS/static-cms/blob/main/CONTRIBUTING.md#pull-requests)!)
Inside the `admin` folder, you'll create two files:
```bash
admin
├ index.html
└ config.yml
```
The first file, `admin/index.html`, is the entry point for the Static CMS admin interface. This means that users navigate to `yoursite.com/admin/` to access it. On the code side, it's a basic HTML starter page that loads the Static CMS JavaScript file from a public CDN and initializes it. The second file, `admin/config.yml`, is the heart of your Static CMS installation, and a bit more complex. The [Configuration](#configuration) section covers the details.
In this example, we pull the `admin/index.html` file from a public CDN.
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
</head>
<body>
<!-- Include the script that builds the page and powers Static CMS -->
<script src="https://unpkg.com/@staticcms/core@%5E1.0.0/dist/static-cms-core.js"></script>
<script>
window.CMS.init();
</script>
</body>
</html>
```
In the code above the `script` is loaded from the `unpkg` CDN. Should there be any issue, `jsDelivr` can be used as an alternative source. Simply set the `src` to `https://cdn.jsdelivr.net/npm/@staticcms/core@%5E1.0.0/dist/static-cms-core.js`
## Configuration
Configuration is different for every site, so we'll break it down into parts. Add all the code snippets in this section to your `admin/config.yml` file. Alternatively, you can use a javascript file (`admin/config.js`) instead of a yaml file. Simply import the javascript config and pass it into your `CMS.init({ config })` call.
### Backend
We're using [Netlify](https://www.netlify.com) for our hosting and authentication in this tutorial, so backend configuration is fairly straightforward.
For GitHub repositories, you can start your Static CMS `config` file with these lines:
<CodeTabs>
```yaml
backend:
name: git-gateway
branch: main # Branch to update (optional; defaults to main)
```
```js
backend: {
name: 'git-gateway',
branch: 'main' // Branch to update (optional; defaults to main)
},
```
</CodeTabs>
_(For GitLab repositories, use [GitLab backend](/docs/gitlab-backend) and for Bitbucket repositories, use [Bitbucket backend](/docs/bitbucket-backend).)_
The configuration above specifies your backend protocol and your publication branch. Git Gateway is an open source API that acts as a proxy between authenticated users of your site and your site repo. (We'll get to the details of that in the [Authentication section](#authentication) below.) If you leave out the `branch` declaration, it defaults to `main`.
### Media and Public Folders
Static CMS allows users to upload images directly within the editor. For this to work, the CMS needs to know where to save them. If you already have an `images` folder in your project, you could use its path, possibly creating an `uploads` sub-folder, for example:
<CodeTabs>
```yaml
# This line should *not* be indented
media_folder: 'images/uploads' # Media files will be stored in the repo under images/uploads
```
```js
media_folder: 'images/uploads', // Media files will be stored in the repo under images/uploads
```
</CodeTabs>
If you're creating a new folder for uploaded media, you'll need to know where your static site generator expects static files. You can refer to the paths outlined above in [App File Structure](#app-file-structure), and put your media folder in the same location where you put the `admin` folder.
Note that the `media_folder` file path is relative to the project root, so the example above would work for Jekyll, GitBook, or any other generator that stores static files at the project root. However, it would not work for Hugo, Next, Hexo, Middleman or others that store static files in a subfolder. Here's an example that could work for a Hugo site:
<CodeTabs>
```yaml
# These lines should *not* be indented
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
```
```js
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
```
</CodeTabs>
The configuration above adds a new setting, `public_folder`. While `media_folder` specifies where uploaded files are saved in the repo, `public_folder` indicates where they are found in the published site. Image `src` attributes use this path, which is relative to the file where it's called. For this reason, we usually start the path at the site root, using the opening `/`.
_If `public_folder` is not set, Static CMS defaults to the same value as `media_folder`, adding an opening `/` if one is not included._
### Collections
Collections define the structure for the different content types on your static site. Since every site is different, the `collections` settings differ greatly from one site to the next.
Let's say your site has a blog, with the posts stored in `_posts/blog`, and files saved in a date-title format, like `1999-12-31-lets-party.md`. Each post begins with settings in yaml-formatted front matter, like so:
```yaml
---
layout: blog
title: "Let's Party"
date: 1999-12-31 11:59:59 -0800
thumbnail: '/images/prince.jpg'
rating: 5
---
This is the post body, where I write about our last chance to party before the Y2K bug destroys us all.
```
Given this example, our `collections` settings would look like this in your Static CMS `config` file:
<CodeTabs>
```yaml
collections:
- name: 'blog' # Used in routes, e.g., /admin/collections/blog
label: 'Blog' # Used in the UI
folder: '_posts/blog' # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
slug: '{{year}}-{{month}}-{{day}}-{{slug}}' # Filename template, e.g., YYYY-MM-DD-title.md
fields: # The fields for each document, usually in front matter
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' }
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- { label: 'Featured Image', name: 'thumbnail', widget: 'image' }
- { label: 'Rating (scale of 1-5)', name: 'rating', widget: 'number' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
```js
collections: [
{
name: 'blog', // Used in routes, e.g., /admin/collections/blog
label: 'Blog', // Used in the UI
folder: '_posts/blog', // The path to the folder where the documents are stored
create: true, // Allow users to create new documents in this collection
slug: '{{year}}-{{month}}-{{day}}-{{slug}}', // Filename template, e.g., YYYY-MM-DD-title.md
fields: [
// The fields for each document, usually in front matter
{ label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' },
{ label: 'Title', name: 'title', widget: 'string' },
{ label: 'Publish Date', name: 'date', widget: 'datetime' },
{ label: 'Featured Image', name: 'thumbnail', widget: 'image' },
{ label: 'Rating (scale of 1-5)', name: 'rating', widget: 'number' },
{ label: 'Body', name: 'body', widget: 'markdown' },
],
},
],
```
</CodeTabs>
Let's break that down:
| Field | Description |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | Post type identifier, used in routes. Must be unique. |
| label | What the admin UI calls the post type. |
| folder | Where files of this type are stored, relative to the repo root. |
| create | Set to `true` to allow users to create new files in this collection. |
| slug | Template for filenames. `{{ year }}`, `{{ month }}`, and `{{ day }}` pulls from the post's `date` field or save date. `{{ slug }}` is a url-safe version of the post's `title`. Default is simply `{{ slug }}`. |
| fields | Fields listed here are shown as fields in the content editor, then saved as front matter at the beginning of the document (except for `body`, which follows the front matter). |
As described above, the `widget` property specifies a built-in or custom UI widget for a given field. When a content editor enters a value into a widget, that value is saved in the document front matter as the value for the `name` specified for that field. A full listing of available widgets can be found in the [Widgets doc](/docs/widgets).
Based on this example, you can go through the post types in your site and add the appropriate settings to your Static CMS `config` file. Each post type should be listed as a separate node under the `collections` field. See the [Collections reference doc](/docs/collection-overview) for more configuration options.
### Filter
The entries for any collection can be filtered based on the value of a single field. The example collection below only shows post entries with the value `en` in the `language` field.
<CodeTabs>
```yaml
collections:
- name: posts
label: Post
folder: _posts
filter:
field: language
value: en
fields:
- name: language
label: Language
```
```js
collections: [
{
name: 'posts',
label: 'Post',
folder: '_posts',
filter: {
field: 'language',
value: 'en',
},
fields: [
{
name: 'language',
label: 'Language',
},
],
},
],
```
</CodeTabs>
## Authentication
Now that you have your Static CMS files in place and configured, all that's left is to enable authentication. We're using the [Netlify](https://www.netlify.com/) platform here because it's one of the quickest ways to get started, but you can learn about other authentication options in the [Backends](/docs/backends-overview) doc.
### Setup on Netlify
Netlify offers a built-in authentication service called Identity. In order to use it, connect your site repo with Netlify.
### Enable Identity and Git Gateway
Netlify's Identity and Git Gateway services allow you to manage CMS admin users for your site without requiring them to have an account with your Git host or commit access on your repo. From your site dashboard on Netlify:
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you want only invited users to access your CMS, but if you're just experimenting, you can leave it open for convenience.
3. If you'd like to allow one-click login with services like Google and GitHub, check the boxes next to the services you'd like to use, under **External providers**.
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This authenticates with your Git host and generates an API access token. In this case, we're leaving the **Roles** field blank, which means any logged in user may access the CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
### Add the Netlify Identity Widget
With the backend set to handle authentication, now you need a frontend interface to connect to it. The open source Netlify Identity Widget is a drop-in widget made for just this purpose. To include the widget in your site, add the following script tag in two places:
```html
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
```
Add this to the `<head>` of your CMS index page at `/admin/index.html`, as well as the `<head>` of your site's main index page. Depending on how your site generator is set up, this may mean you need to add it to the default template, or to a "partial" or "include" template. If you can find where the site stylesheet is linked, that's probably the right place. Alternatively, you can include the script in your site using Netlify's [Script Injection](https://www.netlify.com/docs/inject-analytics-snippets/) feature.
When a user logs in with the Netlify Identity widget, an access token directs to the site homepage. In order to complete the login and get back to the CMS, redirect the user back to the `/admin/` path. To do this, add the following script before the closing `body` tag of your site's main index page:
```html
<script>
if (window.netlifyIdentity) {
window.netlifyIdentity.on('init', user => {
if (!user) {
window.netlifyIdentity.on('login', () => {
document.location.href = '/admin/';
});
}
});
}
</script>
```
Note: This example script requires modern JavaScript and does not work on IE11. For legacy browser support, use function expressions (`function () {}`) in place of the arrow functions (`() => {}`), or use a transpiler such as [Babel](https://babeljs.io/).
## Accessing the CMS
Your site CMS is now fully configured and ready for login!
If you set your registration preference to "Invite only," invite yourself (and anyone else you choose) as a site user. To do this, select the **Identity** tab from your site dashboard, and then select the **Invite users** button. Invited users receive an email invitation with a confirmation link. Clicking the link will take you to your site with a login prompt.
If you left your site registration open, or for return visits after confirming an email invitation, access your site's CMS at `yoursite.com/admin/`.
**Note:** No matter where you access Static CMS — whether running locally, in a staging environment, or in your published site — it always fetches and commits files in your hosted repository (for example, on GitHub), on the branch you configured in your Static CMS `config` file. This means that content fetched in the admin UI matches the content in the repository, which may be different from your locally running site. It also means that content saved using the admin UI saves directly to the hosted repository, even if you're running the UI locally or in staging.
Happy posting!

View File

@ -0,0 +1,19 @@
---
group: Intro
title: Add to Your Site
weight: 3
---
You can adapt Static CMS to a wide variety of projects. It works with any content written in markdown, JSON or YAML files, stored in a repo on [GitHub](https://github.com/), [GitLab](https://gitlab.com/) or [Bitbucket](https://bitbucket.org). You can also create your own custom backend.
You can add Static CMS to your site in two different ways:
## CDN hosting
Adding Static CMS via a public CDN to a site that's built with a common static site generator, like Jekyll, Hugo, Hexo, or Gatsby. Alternatively, is a quick and easy way to get started. You can start from a [template](/docs/start-with-a-template) or use [this guide](/docs/add-to-your-site-cdn) to get started.
## Bundling
An alternative to CDN is directly bundling the StaticCMS package directly into your app. While this require extra setup steps, it give you the greatest level of control over your CMS. It also provides complete support for TypeScript, including for your config file.
See [the bundling guide](/docs/add-to-your-site-bundling) to get started.

View File

@ -0,0 +1,66 @@
---
group: Customization
title: Custom Links & Pages
weight: 60
---
The Static CMS exposes a `window.CMS` global object that you can use to register external links or links custom pages, via `registerAdditionalLink`. The links are displayed at the bottom of the navigation menu in the order they are registered.
### React Components Inline
The `registerPreviewTemplate` requires you to provide a React component. If you have a build process in place for your project, it is possible to integrate with this build process.
However, although possible, it may be cumbersome or even impractical to add a React build phase. For this reason, Static CMS exposes some constructs globally to allow you to create components inline: `h` (alias for React.createElement) as well some basic hooks (`useState`, `useMemo`, `useEffect`, `useCallback`).
**NOTE**: `createClass` is still provided, allowing for the creation of react class components. However it has now been deprecated and will be removed in `v2.0.0`.
## Params
`registerAdditionalLink` takes an `AdditionalLink` object with the following properties:
| Param | Type | Description |
| ------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| id | string | Unique identifier for the link |
| title | string | Display text for the link |
| data | string<br />\| [React Function Component](https://reactjs.org/docs/components-and-props.html) | <ul><li>`string` - The href for the link</li><li>`React Function Component` - A react component to render on the route `/page/[id]`</li></ul> |
| options | object | _Optional_. See [Options](#options) |
### Options
Available options for each additional link are:
| Param | Type | Description |
| -------- | ------ | --------------------------------------------------------------------------------------- |
| iconName | string | The name of a custom registered icon to display. See [Custom Icons](/docs/custom-icons) |
## Examples
### External Links
```js
CMS.registerAdditionalLink({
id: 'example',
title: 'Example.com',
data: 'https://example.com',
options: {
icon: 'page',
},
});
```
### Custom Page
```js
const CustomPage = () => {
return h('div', {}, 'I am a custom page!');
};
CMS.registerAdditionalLink({
id: 'custom-page',
title: 'Custom Page',
data: CustomPage,
options: {
icon: 'page',
},
});
```

View File

@ -0,0 +1,25 @@
---
group: Backends
title: Overview
weight: 1
---
A backend is JavaScript code that allows Static CMS to communicate with a service that stores content - typically a Git host like GitHub or GitLab. It provides functions that Static CMS can use to do things like read and update files using API's provided by the service.
## Backend Configuration
Individual backends provide their own configuration documentation, but there are some configuration options that are common to multiple backends. A full reference is below. Note that these are properties of the `backend` field, and should be nested under that field.
| Name | Type | Default | Description |
| ------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | 'git-gateway'<br />\| 'github'<br />\| 'gitlab'<br />\| 'bitbucket'<br />\| 'test-repo'<br />\| 'proxy' | | The backend git provider |
| repo | string | | Required for `github`, `gitlab`, and `bitbucket` backends. Ignored by `git-gateway`. Follows the pattern `[org-or-username]/[repo-name]` |
| branch | string | `main` | _Optional_. The branch where published content is stored. All CMS commits and PRs are made to this branch |
| api_root | string | GitHub<br />`https://api.github.com`<br /><br />GitLab<br/>`https://gitlab.com/api/v4`<br /><br />Bitbucket<br />`https://api.bitbucket.org/2.0` | _Optional_. The API endpoint. Only necessary in certain cases, like with GitHub Enterprise or self-hosted GitLab |
| site_domain | string | `location.hostname`<br /><br />On `localhost`<br />`cms.netlify.com` | _Optional_. Sets the `site_id` query param sent to the API endpoint. Non-Netlify auth setups will often need to set this for local development to work properly |
| base_url | string | GitHub or Bitbucket<br />`https://api.netlify.com`<br /><br />GitLab<br />`https://gitlab.com` | _Optional_. OAuth client hostname (just the base domain, no path). **Required** when using an external OAuth server or self-hosted GitLab |
| auth_endpoint | string | GitHub or Bitbucket<br />`auth`<br /><br />GitLab<br />`oauth/authorize` | _Optional_. Path to append to `base_url` for authentication requests. |
## Creating a New Backend
Anyone can write a backend, but the API is not yet finalized and documented. If you would like to write your own backend for a service that does not have one currently, Static CMS recommends using the [GitHub backend](https://github.com/StaticJsCMS/static-cms/tree/main/core/src/backends/github) as a reference for API and best practices.

View File

@ -0,0 +1,520 @@
---
group: Intro
title: Beta Features
weight: 200
---
<Alert severity="error">Features on this page may be broken or may not function as expected. We are working to test and fix the features as well as update the docs. Use at your own risk.</Alert>
Static CMS runs new functionality in an open beta format from time to time. That means that this functionality is totally available for use, an it might be ready for primetime, but it could break or change without notice.
**Use these features at your own risk.**
## i18n Support
The CMS can provide a side by side interface for authoring content in multiple languages.
Configuring the CMS for i18n support requires top level configuration, collection level configuration and field level configuration.
### Top level configuration
<CodeTabs>
```yaml
i18n:
# Required and can be one of multiple_folders, multiple_files or single_file
# multiple_folders - persists files in `<folder>/<locale>/<slug>.<extension>`
# multiple_files - persists files in `<folder>/<slug>.<locale>.<extension>`
# single_file - persists a single file in `<folder>/<slug>.<extension>`
structure: multiple_folders
# Required - a list of locales to show in the editor UI
locales: [en, de, fr]
# Optional, defaults to the first item in locales.
# The locale to be used for fields validation and as a baseline for the entry.
defaultLocale: en
```
```js
i18n: {
/**
* Required and can be one of multiple_folders, multiple_files or single_file
* multiple_folders - persists files in `<folder>/<locale>/<slug>.<extension>`
* multiple_files - persists files in `<folder>/<slug>.<locale>.<extension>`
* single_file - persists a single file in `<folder>/<slug>.<extension>`
*/
structure: 'multiple_folders',
// Required - a list of locales to show in the editor UI
locales: ['en', 'de', 'fr'],
/**
* Optional, defaults to the first item in locales.
* The locale to be used for fields validation and as a baseline for the entry.
*/
defaultLocale: 'en'
},
```
</CodeTabs>
### Collection level configuration
<CodeTabs>
```yaml
collections:
- name: i18n_content
# same as the top level, but all fields are optional and defaults to the top level
# can also be a boolean to accept the top level defaults
i18n: true
```
```js
collections: [
{
name: 'i18n_content',
/**
* same as the top level, but all fields are optional and defaults to the top level
* can also be a boolean to accept the top level defaults
*/
i18n: true
},
],
```
</CodeTabs>
When using a file collection, you must also enable i18n for each individual file:
<CodeTabs>
```yaml
collections:
- name: pages
label: Pages
# Configure i18n for this collection.
i18n:
structure: single_file
locales: [en, de, fr]
files:
- name: about
label: About Page
file: site/content/about.yml
# Enable i18n for this file.
i18n: true
fields:
- { label: Title, name: title, widget: string, i18n: true }
```
```js
collections: [
{
name: 'pages',
label: 'Pages',
// Configure i18n for this collection.
i18n: {
structure: 'single_file',
locales: ['en', 'de', 'fr']
},
files: [
{
name: 'about',
label: 'About Page',
file: 'site/content/about.yml',
// Enable i18n for this file.
i18n: true,
fields: [
{ label: 'Title', name: 'title', widget: 'string', i18n: true }
],
},
],
},
],
```
</CodeTabs>
### Field level configuration
<CodeTabs>
```yaml
fields:
- label: Title
name: title
widget: string
# same as 'i18n: translate'. Allows translation of the title field
i18n: true
- label: Date
name: date
widget: datetime
# The date field will be duplicated from the default locale.
i18n: duplicate
- label: Body
name: body
# The markdown field will be omitted from the translation.
widget: markdown
```
```js
fields: [
{
label: 'Title',
name: 'title',
widget: 'string',
// same as 'i18n: translate'. Allows translation of the title field
i18n: true
},
{
label: 'Date',
name: 'date',
widget: 'datetime',
// The date field will be duplicated from the default locale.
i18n: 'duplicate'
},
{
label: 'Body',
name: 'body',
// The markdown field will be omitted from the translation.
widget: 'markdown'
},
],
```
</CodeTabs>
Example configuration:
<CodeTabs>
```yaml
i18n:
structure: multiple_folders
locales: [en, de, fr]
collections:
- name: posts
label: Posts
folder: content/posts
create: true
i18n: true
fields:
- label: Title
name: title
widget: string
i18n: true
- label: Date
name: date
widget: datetime
i18n: duplicate
- label: Body
name: body
widget: markdown
```
```js
i18n: {
structure: 'multiple_folders',
locales: ['en', 'de', 'fr']
},
collections: [
{
name: 'posts',
label: 'Posts',
folder: 'content/posts',
create: true,
i18n: true,
fields: [
{ label: 'Title', name: 'title', widget: 'string', i18n: true },
{ label: 'Date', name: 'date', widget: 'datetime', i18n: 'duplicate' },
{ label: 'Body', name: 'body', widget: 'markdown' },
],
},
],
```
</CodeTabs>
### Limitations
1. File collections support only `structure: single_file`.
2. List widgets only support `i18n: true`. `i18n` configuration on sub fields is ignored.
3. Object widgets only support `i18n: true` and `i18n` configuration should be done per field:
<CodeTabs>
```yaml
- label: 'Object'
name: 'object'
widget: 'object'
i18n: true
fields:
- { label: 'String', name: 'string', widget: 'string', i18n: true }
- { label: 'Date', name: 'date', widget: 'datetime', i18n: duplicate }
- { label: 'Boolean', name: 'boolean', widget: 'boolean', i18n: duplicate }
- {
label: 'Object',
name: 'object',
widget: 'object',
i18n: true,
field: { label: 'String', name: 'string', widget: 'string', i18n: duplicate },
}
```
```js
{
label: 'Object',
name: 'object',
widget: 'object',
i18n: true,
fields: [
{ label: 'String', name: 'string', widget: 'string', i18n: true },
{ label: 'Date', name: 'date', widget: 'datetime', i18n: 'duplicate' },
{ label: 'Boolean', name: 'boolean', widget: 'boolean', i18n: 'duplicate' },
{
label: 'Object',
name: 'object',
widget: 'object',
i18n: true,
field: { label: 'String', name: 'string', widget: 'string', i18n: 'duplicate' },
},
],
},
```
</CodeTabs>
## Folder Collections Path
See [Folder Collections Path](/docs/collection-types#folder-collections-path).
## Nested Collections
Seed [Nested Collections](/docs/collection-types#nested-collections).
## Custom Mount Element
Static CMS always creates its own DOM element for mounting the application, which means it always takes over the entire page, and is generally inflexible if you're trying to do something creative, like injecting it into a shared context.
You can now provide your own element for Static CMS to mount in by setting the target element's ID as `nc-root`. If Static CMS finds an element with this ID during initialization, it will mount within that element instead of creating its own.
## Commit Message Templates
You can customize the templates used by Static CMS to generate commit messages by setting the `commit_messages` option under `backend` in your Static CMS `config`.
Template tags wrapped in curly braces will be expanded to include information about the file changed by the commit. For example, `{{path}}` will include the full path to the file changed.
Setting up your Static CMS `config` to recreate the default values would look like this:
<CodeTabs>
```yaml
backend:
commit_messages:
create: Create {{collection}} "{{slug}}"
update: Update {{collection}} "{{slug}}"
delete: Delete {{collection}} "{{slug}}"
uploadMedia: Upload "{{path}}"
deleteMedia: Delete "{{path}}"
```
```js
backend: {
commit_messages: {
create: 'Create {{collection}} "{{slug}}"',
update: 'Update {{collection}} "{{slug}}"',
delete: 'Delete {{collection}} "{{slug}}"',
uploadMedia: 'Upload "{{path}}"',
deleteMedia: 'Delete "{{path}}"',
},
},
```
</CodeTabs>
Static CMS generates the following commit types:
| Commit type | When is it triggered? | Available template tags |
| ------------- | ---------------------------- | ----------------------------------------------------------- |
| `create` | A new entry is created | `slug`, `path`, `collection`, `author-login`, `author-name` |
| `update` | An existing entry is changed | `slug`, `path`, `collection`, `author-login`, `author-name` |
| `delete` | An existing entry is deleted | `slug`, `path`, `collection`, `author-login`, `author-name` |
| `uploadMedia` | A media file is uploaded | `path`, `author-login`, `author-name` |
| `deleteMedia` | A media file is deleted | `path`, `author-login`, `author-name` |
Template tags produce the following output:
- `{{slug}}`: the url-safe filename of the entry changed
- `{{collection}}`: the name of the collection containing the entry changed
- `{{path}}`: the full path to the file changed
- `{{message}}`: the relevant message based on the current change (e.g. the `create` message when an entry is created)
- `{{author-login}}`: the login/username of the author
- `{{author-name}}`: the full name of the author (might be empty based on the user's profile)
## Image widget file size limit
You can set a limit to as what the maximum file size of a file is that users can upload directly into a image field.
Example config:
<CodeTabs>
```yaml
- label: 'Featured Image'
name: 'thumbnail'
widget: 'image'
default: '/uploads/chocolate-dogecoin.jpg'
media_library:
config:
max_file_size: 512000 # in bytes, only for default media library
```
```js
{
label: 'Featured Image',
name: 'thumbnail',
widget: 'image',
default: '/uploads/chocolate-dogecoin.jpg',
media_library: {
config: {
max_file_size: 512000 // in bytes, only for default media library
},
},
},
```
</CodeTabs>
## Summary string template transformations
You can apply transformations on fields in a summary string template using filter notation syntax.
Example config:
<CodeTabs>
```yaml
collections:
- name: 'posts'
label: 'Posts'
folder: '_posts'
summary: "{{title | upper}} - {{date | date('YYYY-MM-DD')}} - {{body | truncate(20, '***')}}"
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
```js
collections: [
{
name: 'posts',
label: 'Posts',
folder: '_posts',
summary: "{{title | upper}} - {{date | date('YYYY-MM-DD')}} - {{body | truncate(20, '***')}}",
fields: [
{ label: 'Title', name: 'title', widget: 'string' },
{ label: 'Publish Date', name: 'date', widget: 'datetime' },
{ label: 'Body', name: 'body', widget: 'markdown' },
],
},
],
```
</CodeTabs>
The above config will transform the title field to uppercase and format the date field using `YYYY-MM-DD` format.
Available transformations are `upper`, `lower`, `date('<format>')`, `default('defaultValue')`, `ternary('valueForTrue','valueForFalse')` and `truncate(<number>)`/`truncate(<number>, '<string>')`
## Registering to CMS Events
You can execute a function when a specific CMS event occurs.
Example usage:
```javascript
CMS.registerEventListener({
name: 'prePublish',
handler: ({ author, entry }) => console.info(JSON.stringify({ author, data: entry.data })),
});
```
Supported events are `prePublish`, `postPublish`, `preSave` and `postSave`. The `preSave` hook can be used to modify the entry data like so:
```javascript
CMS.registerEventListener({
name: 'preSave',
handler: ({ entry }) => {
return entry.data.set('title', 'new title');
},
});
```
## Dynamic Default Values
When linking to `/admin/#/collections/posts/new` you can pass URL parameters to pre-populate an entry.
For example given the configuration:
<CodeTabs>
```yaml
collections:
- name: posts
label: Posts
folder: content/posts
create: true
fields:
- label: Title
name: title
widget: string
- label: Object
name: object
widget: object
fields:
- label: Title
name: title
widget: string
- label: body
name: body
widget: markdown
```
```js
collections: [
{
name: 'posts',
label: 'Posts',
folder: 'content/posts',
create: true,
fields: [
{
label: 'Title',
name: 'title',
widget: 'string'
},
{
label: 'Object',
name: 'object',
widget: 'object',
fields: [
{
label: 'Title',
name: 'title',
widget: 'string'
}
],
},
{
label: 'body',
name: 'body',
widget: 'markdown'
},
],
},
],
```
</CodeTabs>
clicking the following link: `/#/collections/posts/new?title=first&object.title=second&body=%23%20content`
will open the editor for a new post with the `title` field populated with `first`, the nested `object.title` field
with `second` and the markdown `body` field with `# content`.
**Note:** URL Encoding might be required for certain values (e.g. in the previous example the value for `body` is URL encoded).

View File

@ -0,0 +1,31 @@
---
group: Backends
title: Bitbucket
weight: 30
---
- **Name**: `bitbucket`
For repositories stored on Bitbucket, the `bitbucket` backend allows CMS users to log in directly with their Bitbucket account. Note that all users must have write access to your content repository for this to work.
## Authentication
To enable Bitbucket authentication it:
1. Follow the authentication provider setup steps in the [Netlify docs](https://www.netlify.com/docs/authentication-providers/#using-an-authentication-provider).
2. Add the following lines to your Static CMS `config` file:
<CodeTabs>
```yaml
backend:
name: bitbucket
repo: owner-name/repo-name # Path to your Bitbucket repository
```
```js
backend: {
name: 'bitbucket',
repo: 'owner-name/repo-name', // Path to your Bitbucket repository
},
```
</CodeTabs>

View File

@ -0,0 +1,236 @@
---
group: Media
title: Cloudinary
weight: 10
---
## <img src="https://img.shields.io/badge/-Beta%20Feature-blue" alt="Beta Feature. Use at your own risk" title="Beta Feature. Use at your own risk" />
Cloudinary is a digital asset management platform with a broad feature set, including support for responsive image generation and url based image transformation. They also provide a powerful media library UI for managing assets, and tools for organizing your assets into a hierarchy.
The Cloudinary media library integration for Static CMS uses Cloudinary's own media library interface within Static CMS. To get started, you'll need a Cloudinary account and Static CMS 2.3.0 or greater.
## Creating a Cloudinary Account
You can [sign up for Cloudinary](https://cloudinary.com/users/register/free) for free. Once you're logged in, you'll need to retrieve your Cloud name and API key from the upper left corner of the Cloudinary console.
![Cloudinary console screenshot](/img/cloudinary-console-details.webp)
## Connecting Cloudinary
To use the Cloudinary media library within Static CMS, you'll need to update your Static CMS configuration file with the information from your Cloudinary account:
<CodeTabs>
```yaml
media_library:
name: cloudinary
config:
cloud_name: your_cloud_name
api_key: your_api_key
```
```js
media_library: {
name: 'cloudinary',
config: {
cloud_name: 'your_cloud_name',
api_key: 'your_api_key'
},
},
```
</CodeTabs>
**Note:** The user must be logged in to the Cloudinary account connected to the `api_key` used in your Static CMS configuration.
### Security Considerations
Although this setup exposes the `cloud_name` and `api_key` publicly via the `config` endpoint, this information is not sensitive. Any integration of the Cloudinary media library requires this information to be exposed publicly. To use this library or use the restricted Cloudinary API endpoints, the user must have access to the Cloudinary account login details or the `api_secret` associated with the `cloud_name` and `api_key`.
## Static CMS configuration options
The following options are specific to the Static CMS integration for Cloudinary:
- **`output_filename_only`**: _(default: `false`)_\
By default, the value provided for a selected image is a complete URL for the asset on Cloudinary's CDN. Setting `output_filename_only` to `true` will instead produce just the filename (e.g. `image.jpg`). This should be `true` if you will be directly embedding cloudinary transformation urls in page templates. Refer to [Inserting Cloudinary URL in page templates](#inserting-cloudinary-url-in-page-templates).
- **`use_transformations`**: _(default: `true`)_\
If `true`, uses derived url when available (the url will have image transformation segments included). Has no effect if `output_filename_only` is set to `true`.
- **`use_secure_url`**: _(default: `true`)_\
Controls whether an `http` or `https` URL is provided. Has no effect if `output_filename_only` is set to `true`.
## Cloudinary configuration options
The following options are used to configure the media library. All options are listed in Cloudinary's [media library documentation](https://cloudinary.com/documentation/media_library_widget#3_set_the_configuration_options), but only options listed below are available or recommended for the Static CMS integration:
### Authentication
- `cloud_name`
- `api_key`
### Media library behavior
- `default_transformations` _\- only the first [image transformation](#image-transformations) is used, be sure to use the `SDK Parameter` column transformation names from the_ [_transformation reference_](https://cloudinary.com/documentation/image_transformation_reference)
- `max_files` _\- has no impact on images inside the [markdown widget](/docs/widgets/#markdown)_. Refer to [media library documentation](https://cloudinary.com/documentation/media_library_widget#3_set_the_configuration_options) for details on this property
- `multiple` _\- has no impact on images inside the [markdown widget](/docs/widgets/#markdown)_. Refer to [media library documentation](https://cloudinary.com/documentation/media_library_widget#3_set_the_configuration_options) for details on this property
## Image transformations
The Cloudinary integration allows images to be transformed in two ways: directly within Static CMS via [Cloudinary's Media Library](#transforming-images-via-media-library), and separately from the CMS via Cloudinary's [dynamic URL's](https://cloudinary.com/documentation/image_transformations#delivering_media_assets_using_dynamic_urls) by [inserting cloudinary urls](#inserting-cloudinary-url-in-page-templates).
### Transforming Images
If you transform and insert images from within the Cloudinary media library, the transformed image URL will be output by default. This gives the editor complete freedom to make changes to the image output.
There are two ways to configure image transformation via media library - [globally](#global-configuration) and per [field](#field-configuration). Global options will be overridden by field options.
#### Global configuration
Global configuration, which is meant to affect the Cloudinary widget at all times, can be provided
as seen below, under the primary `media_library` property. Settings applied here will affect every
instance of the Cloudinary widget.
<CodeTabs>
```yaml
# global
media_library:
name: cloudinary
output_filename_only: false
config:
default_transformations:
- - fetch_format: auto
width: 160
quality: auto
crop: scale
```
```js
// global
media_library: {
name: 'cloudinary',
output_filename_only: false,
config: {
default_transformations: [
[
{
fetch_format: 'auto',
width: 160,
quality: 'auto',
crop: 'scale'
}
],
],
},
},
```
</CodeTabs>
#### Field configuration
Configuration can also be provided for individual fields that use the media library. The structure
is very similar to the global configuration, except the settings are added to an individual `field`.
For example:
<CodeTabs>
```yaml
# field
fields: # The fields each document in this collection have
- label: 'Cover Image'
name: 'image'
widget: 'image'
required: false
tagtitle: ''
media_library:
config:
default_transformations:
- fetch_format: auto
width: 300
quality: auto
crop: fill
effect: grayscale
```
```js
// field
fields: [
{
label: 'Cover Image',
name: 'image',
widget: 'image',
required: false,
tagtitle: '',
media_library: {
config: {
default_transformations: [
{
fetch_format: 'auto',
width: 300,
quality: 'auto',
crop: 'fill',
effect: 'grayscale',
},
],
},
},
},
],
```
</CodeTabs>
## Inserting Cloudinary URL in page templates
If you prefer to provide direction so that images are transformed in a specific way, or dynamically retrieve images based on viewport size, you can do so by providing your own base Cloudinary URL and only storing the asset filenames in your content:
- Either globally or for specific fields, configure the Cloudinary extension to only output the asset filename
**Global**
<CodeTabs>
```yaml
# global
media_library:
name: cloudinary
output_filename_only: true
```
```js
// global
media_library: {
name: 'cloudinary',
output_filename_only: true,
},
```
</CodeTabs>
**Field**
<CodeTabs>
```yaml
# field
media_library:
name: cloudinary
output_filename_only: true
```
```js
// field
media_library: {
name: 'cloudinary',
output_filename_only: true,
},
```
</CodeTabs>
- Provide a dynamic URL in the site template
```handlebars
{{! handlebars example }}
<img
src='https://res.cloudinary.com/<cloud_name>/<resource_type>/<type>/<transformations>/{{image}}'
/>
```
Your dynamic URL can be formed conditionally to provide any desired transformations - please see Cloudinary's [image transformation reference](https://cloudinary.com/documentation/image_transformation_reference) for available transformations.

View File

@ -0,0 +1,350 @@
---
group: Collections
title: Collections Configuration
weight: 9
---
`collections` accepts a list of collection objects, each with the following options
| Name | Type | Default | Description |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | | Unique identifier for the collection, used as the key when referenced in other contexts (like the [relation widget](/docs/widgets/#relation)) |
| identifier_field | string | `'title'` | _Optional_. See [identifier_field](#identifier_field) below |
| label | string | `name` | _Optional_. Label for the collection in the editor UI |
| label_singular | string | `label` | _Optional_. Singular label for certain elements in the editor |
| icon | string | | _Optional_. Unique name of icon to use in main menu. See [Custom Icons](/docs/custom-icons) |
| description | string | | _Optional_. Text displayed below the label when viewing a collection |
| files or folder | [Collection Files](/docs/collection-types#file-collections)<br />\| [Collection Folder](/docs/collection-types#folder-collections) | | **Requires one of these**: Specifies the collection type and location; details in [Collection Types](/docs/collection-types) |
| filter | FilterRule | | _Optional_. Filter for [Folder Collections](/docs/collection-types#folder-collections) |
| create | boolean | `false` | _Optional_. **For [Folder Collections](/docs/collection-types#folder-collections) only**<br />`true` - Allows users to create new items in the collection |
| hide | boolean | `false` | _Optional_. `true` hides a collection in the CMS UI. Useful when using the relation widget to hide referenced collections |
| delete | boolean | `true` | _Optional_. `false` prevents users from deleting items in a collection |
| extension | string | | _Optional_. See [extension](#extension-and-format) below |
| format | 'yaml'<br />\| 'yml'<br />\| 'json'<br />\| 'frontmatter'<br />\| 'json-frontmatter'<br />\| 'yaml-frontmatter' | | _Optional_. See [format](#extension-and-format) below |
| frontmatter_delimiter | string<br />\| [string, string] | | _Optional_. See [frontmatter_delimiter](#frontmatter_delimiter) below |
| slug | string | | _Optional_. See [slug](#slug) below |
| fields (required) | Field | | _Optional_. See [fields](#fields) below. Ignored if [Files Collection](/docs/collection-types#file-collections) |
| editor | EditorConfig | | _Optional_. See [editor](#editor) below |
| summary | string | | _Optional_. See [summary](#summary) below |
| sortable_fields | SortableFields | | _Optional_. See [sortable_fields](#sortable_fields) below |
| view_filters | ViewFilter | | _Optional_. See [view_filters](#view_filters) below |
| view_groups | ViewGroup | | _Optional_. See [view_groups](#view_groups) below |
## `identifier_field`
Static CMS expects every entry to provide a field named `"title"` that serves as an identifier for the entry. The identifier field serves as an entry's title when viewing a list of entries, and is used in [slug](#slug) creation. If you would like to use a field other than `"title"` as the identifier, you can set `identifier_field` to the name of the other field.
<CodeTabs>
```yaml
collections:
- name: posts
identifier_field: name
```
```js
collections: [
{
name: 'posts',
identifier_field: 'name',
},
],
```
</CodeTabs>
## `extension` and `format`
These settings determine how collection files are parsed and saved. Both are optional—Static CMS will attempt to infer your settings based on existing items in the collection. If your collection is empty, or you'd like more control, you can set these fields explicitly.
`extension` determines the file extension searched for when finding existing entries in a folder collection and it determines the file extension used to save new collection items. It accepts the following values: `yml`, `yaml`, `json`, `md`, `markdown`, `html`.
You may also specify a custom `extension` not included in the list above, as long as the collection files can be parsed and saved in one of the supported formats below.
`format` determines how collection files are parsed and saved. It will be inferred if the `extension` field or existing collection file extensions match one of the supported extensions above. It accepts the following values:
- `yml` or `yaml`: parses and saves files as YAML-formatted data files; saves with `yml` extension by default
- `json`: parses and saves files as JSON-formatted data files; saves with `json` extension by default
- `frontmatter`: parses files and saves files with data frontmatter followed by an unparsed body text (edited using a `body` field); saves with `md` extension by default; default for collections that can't be inferred. Collections with `frontmatter` format (either inferred or explicitly set) can parse files with frontmatter in YAML or JSON format. However, they will be saved with YAML frontmatter.
- `yaml-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved only as YAML, followed by unparsed body text. The default delimiter for this option is `---`.
- `json-frontmatter`: same as the `frontmatter` format above, except frontmatter will be both parsed and saved as JSON, followed by unparsed body text. The default delimiter for this option is `{` `}`.
## `frontmatter_delimiter`
If you have an explicit frontmatter format declared, this option allows you to specify a custom delimiter like `~~~`. If you need different beginning and ending delimiters, you can use an array like `["(", ")"]`.
## `slug`
For folder collections where users can create new items, the `slug` option specifies a template for generating new filenames based on a file's creation date and `title` field. (This means that all collections with `create: true` must have a `title` field (a different field can be used via [`identifier_field`](#identifier_field)).
The slug template can also reference a field value by name, eg. `{{title}}`. If a field name conflicts with a built in template tag name - for example, if you have a field named `slug`, and would like to reference that field via `{{slug}}`, you can do so by adding the explicit `fields.` prefix, eg. `{{fields.slug}}`.
**Available Template Tags**
- Any field can be referenced by wrapping the field name in double curly braces, eg. `{{author}}`
- `{{slug}}`: a url-safe version of the `title` field (or identifier field) for the file
- `{{year}}`: 4-digit year of the file creation date
- `{{month}}`: 2-digit month of the file creation date
- `{{day}}`: 2-digit day of the month of the file creation date
- `{{hour}}`: 2-digit hour of the file creation date
- `{{minute}}`: 2-digit minute of the file creation date
- `{{second}}`: 2-digit second of the file creation date
### Examples
#### Basic Example
<CodeTabs>
```yaml
slug: '{{year}}-{{month}}-{{day}}_{{slug}}'
```
```js
slug: '{{year}}-{{month}}-{{day}}_{{slug}}',
```
</CodeTabs>
#### Field Names
<CodeTabs>
```yaml
slug: '{{year}}-{{month}}-{{day}}_{{title}}_{{some_other_field}}'
```
```js
slug: '{{year}}-{{month}}-{{day}}_{{title}}_{{some_other_field}}',
```
</CodeTabs>
#### Field Name That Conflicts With Template Tag
<CodeTabs>
```yaml
slug: '{{year}}-{{month}}-{{day}}_{{fields.slug}}'
```
```js
slug: '{{year}}-{{month}}-{{day}}_{{fields.slug}}',
```
</CodeTabs>
## `fields`
_Ignored if [Files Collection](/docs/collection-types#file-collections)_
The `fields` option maps editor UI widgets to field-value pairs in the saved file. The order of the fields in your Static CMS `config` file determines their order in the editor UI and in the saved file.
`fields` accepts a list of widgets. See [widgets](/docs/widgets) for more details.
In files with frontmatter, one field should be named `body`. This special field represents the section of the document (usually markdown) that comes after the frontmatter.
<CodeTabs>
```yaml
fields:
- label: "Title"
name: "title"
widget: "string"
pattern: ['.{20,}', "Must have at least 20 characters"]
- {label: "Layout", name: "layout", widget: "hidden", default: "blog"}
- {label: "Featured Image", name: "thumbnail", widget: "image", required: false}
- {label: "Body", name: "body", widget: "markdown", comment: 'This is a multiline\ncomment' }
```
```js
fields: [
{ label: 'Title', name: 'title', widget: 'string', pattern: ['.{20,}', 'Must have at least 20 characters'] },
{ label: 'Layout', name: 'layout', widget: 'hidden', default: 'blog' },
{ label: 'Featured Image', name: 'thumbnail', widget: 'image', required: false },
{ label: 'Body', name: 'body', widget: 'markdown', comment: 'This is a multiline\\ncomment' },
],
```
</CodeTabs>
## `editor`
This setting changes options for the editor view of a collection or a file inside a files collection. It has the following options:
| Name | Type | Default | Description |
| ------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| preview | boolean | `true` | Set to `false` to disable the preview pane for this collection or file |
| frame | boolean | `true` | <ul><li>`true` - Previews render in a frame</li><li>`false` - Previews render directly in your app</li></ul> |
<CodeTabs>
```yaml
editor:
preview: false
frame: false
```
```js
editor: {
preview: false,
frame: false,
},
```
</CodeTabs>
**Note**: Setting this as a top level configuration will set the default for all collections
## `summary`
This setting allows the customization of the collection list view. Similar to the `slug` field, a string with templates can be used to include values of different fields, e.g. `{{title}}`. This option over-rides the default of `title` field and `identifier_field`.
**Available Template Tags**
Template tags are the same as those for [slug](#slug), with the following additions:
- `{{dirname}}` The path to the file's parent directory, relative to the collection's `folder`.
- `{{filename}}` The file name without the extension part.
- `{{extension}}` The file extension.
- `{{commit_date}}` The file commit date on supported backends (git based backends).
- `{{commit_author}}` The file author date on supported backends (git based backends).
<CodeTabs>
```yaml
summary: 'Version: {{version}} - {{title}}'
```
```js
summary: 'Version: {{version}} - {{title}}',
```
</CodeTabs>
## `sortable_fields`
An optional object with the following options:
| Name | Type | Default | Description |
| ------- | --------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
| fields | list of string | | A list of sort fields to show in the UI |
| default | SortableFieldsDefault | | _Optional_. The default field and direction to sort the collection. See [Default Sort](#default-sort) for details |
Defaults to inferring `title`, `date`, `author` and `description` fields and will also show `Update On` sort field in git based backends.
When `author` field can't be inferred commit author will be used.
<CodeTabs>
```yaml
# use dot notation for nested fields
sortable_fields:
fields: ['commit_date', 'title', 'commit_author', 'language.en']
```
```js
// use dot notation for nested fields
sortable_fields: {
fields: ['commit_date', 'title', 'commit_author', 'language.en'],
},
```
</CodeTabs>
### Default Sort
| Name | Type | Default | Description |
| --------- | ----------------------------------------------- | ----------- | --------------------------------- |
| field | string | | The field to sort |
| direction | 'Ascending'<br />\| 'Descending'<br />\| 'None' | `Ascending` | _Optional_. The direction to sort |
<CodeTabs>
```yaml
# use dot notation for nested fields
sortable_fields:
fields: ['commit_date', 'title', 'commit_author', 'language.en']
default:
field: commit_date
direction: Descending
```
```js
// use dot notation for nested fields
sortable_fields: {
fields: ['commit_date', 'title', 'commit_author', 'language.en'],
default: {
field: 'commit_date',
direction: 'Descending'
}
},
```
</CodeTabs>
## `view_filters`
An optional list of predefined view filters to show in the UI.
Defaults to an empty list.
<CodeTabs>
```yaml
view_filters:
- label: "Alice's and Bob's Posts"
field: author
pattern: 'Alice|Bob'
- label: 'Posts published in 2020'
field: date
pattern: '2020'
- label: Drafts
field: draft
pattern: true
```
```js
view_filters: [
{
label: "Alice's and Bob's Posts",
field: 'author',
pattern: 'Alice|Bob',
},
{
label: 'Posts published in 2020',
field: 'date',
pattern: '2020',
},
{
label: 'Drafts',
field: 'draft',
pattern: true,
},
],
```
</CodeTabs>
## `view_groups`
An optional list of predefined view groups to show in the UI.
Defaults to an empty list.
<CodeTabs>
```yaml
view_groups:
- label: Year
field: date
# groups items based on the value matched by the pattern
pattern: \d{4}
- label: Drafts
field: draft
```
```js
view_groups: [
{
label: 'Year',
field: 'date',
pattern: '\\d{4}',
},
{
label: 'Drafts',
field: 'draft',
},
],
```
</CodeTabs>

View File

@ -0,0 +1,406 @@
---
group: Collections
title: Collection Types
weight: 10
---
All editable content types are defined in the `collections` field of your `config` file, and display in the left sidebar of the Content page of the editor UI.
Collections come in two main types: `folder` and `files`.
## Folder collections
Folder collections represent one or more files with the same format, fields, and configuration options, all stored within the same folder in the repository. You might use a folder collection for blog posts, product pages, author data files, etc.
Unlike file collections, folder collections have the option to allow editors to create new items in the collection. This is set by the boolean `create` field.
**Note:** Folder collections must have at least one field with the name `title` for creating new entry slugs. That field should use the default `string` widget. The `label` for the field can be any string value. If you wish to use a different field as your identifier, set `identifier_field` to the field name. See the [Collections reference doc](/docs/collection-overview) for details on how collections and fields are configured. If you forget to add this field, you will get an error that your collection "must have a field that is a valid entry identifier".
### Examples
#### Basic
<CodeTabs>
```yaml
collections:
- name: blog
label: Blog
folder: _posts/blog
create: true
fields:
- name: title
label: Title
widget: string
- name: date
label: Publish Date
widget: datetime
- name: thumbnail
label: Featured Image
widget: image
- name: body
label: Body
widget: 'markdown'
```
```js
collections: [
{
name: 'blog',
label: 'Blog',
folder: '_posts/blog',
create: true,
fields: [
{ name: 'title', label: 'Title', widget: 'string' },
{ name: 'date', label: 'Publish Date', widget: 'datetime' },
{ name: 'thumbnail', label: 'Featured Image', widget: 'image' },
{ name: 'body', label: 'Body', widget: 'markdown' },
],
},
],
```
</CodeTabs>
#### With Identifier Field
<CodeTabs>
```yaml
- name: 'blog'
label: 'Blog'
folder: '_posts/blog'
create: true
identifier_field: name
fields:
- name: name
label: Name
widget: string
- name: date
label: Publish Date
widget: datetime
- name: thumbnail
label: Featured Image
widget: image
- name: body
label: Body
widget: markdown
```
```js
{
name: 'blog',
label: 'Blog',
folder: '_posts/blog',
create: true,
identifier_field: 'name',
fields: [
{ name: 'name', label: 'Name', widget: 'string' },
{ name: 'date', label: 'Publish Date', widget: 'datetime' },
{ name: 'thumbnail', label: 'Featured Image', widget: 'image' },
{ name: 'body', label: 'Body', widget: 'markdown' },
],
},
```
</CodeTabs>
### Filtered folder collections
The entries for any folder collection can be filtered based on the value of a single field. By filtering a folder into different collections, you can manage files with different fields, options, extensions, etc. in the same folder.
The `filter` option requires two fields:
- `field`: The name of the collection field to filter on.
- `value`: The desired field value.
The example below creates two collections in the same folder, filtered by the `language` field. The first collection includes posts with `language: en`, and the second, with `language: es`.
<CodeTabs>
```yaml
collections:
- name: 'english_posts'
label: 'Blog in English'
folder: '_posts'
create: true
filter:
field: language
value: en
fields:
- name: language
label: Language
widget: select
options: ['en', 'es']
- name: title
label: Title
widget: string
- name: body
label: Content
widget: markdown
- name: spanish_posts
label: Blog en Español
folder: _posts
create: true
filter:
field: language
value: es
fields:
- name: language
label: Lenguaje
widget: select
options: ['en', 'es']
- name: title
label: Titulo
widget: string
- name: body
label: Contenido
widget: markdown
```
```js
collections: [
{
name: 'english_posts',
label: 'Blog in English',
folder: '_posts',
create: true,
filter: { field: 'language', value: 'en' },
fields: [
{ name: 'language', label: 'Language', widget: 'select', options: ['en', 'es'] },
{ name: 'title', label: 'Title', widget: 'string' },
{ name: 'body', label: 'Content', widget: 'markdown' },
],
},
{
name: 'spanish_posts',
label: 'Blog en Español',
folder: '_posts',
create: true,
filter: { field: 'language', value: 'es' },
fields: [
{ name: 'language', label: 'Lenguaje', widget: 'select', options: ['en', 'es'] },
{ name: 'title', label: 'Titulo', widget: 'string' },
{ name: 'body', label: 'Contenido', widget: 'markdown' },
],
},
],
```
</CodeTabs>
### Folder Collections Path <img src="https://img.shields.io/badge/-Beta%20Feature-blue" alt="Beta Feature. Use at your own risk" title="Beta Feature. Use at your own risk" />
By default the CMS stores folder collection content under the folder specified in the collection setting.
For example configuring `folder: posts` for a collection will save the content under `posts/post-title.md`.
You can now specify an additional `path` template (similar to the `slug` template) to control the content destination.
This allows saving content in subfolders, e.g. configuring `path: '{{year}}/{{slug}}'` will save the content under `posts/2019/post-title.md`.
#### Media and Public Folder
By default the CMS stores media files for all collections under a global `media_folder` directory as specified in the configuration.
When using the global `media_folder` directory any entry field that points to a media file will use the absolute path to the published file as designated by the `public_folder` configuration.
For example configuring:
<CodeTabs>
```yaml
media_folder: static/media
public_folder: /media
```
```js
media_folder: 'static/media',
public_folder: '/media'
```
</CodeTabs>
And saving an entry with an image named `image.png` will result in the image being saved under `static/media/image.png` and relevant entry fields populated with the value of `/media/image.png`.
Some static site generators (e.g. Gatsby) work best when using relative image paths.
This can now be achieved using a per collection `media_folder` configuration which specifies a relative media folder for the collection.
For example, the following configuration will result in media files being saved in the same directory as the entry, and the image field being populated with the relative path to the image.
<CodeTabs>
```yaml
media_folder: static/media
public_folder: /media
collections:
- name: posts
label: Posts
label_singular: 'Post'
folder: content/posts
path: '{{slug}}/index'
media_folder: ''
public_folder: ''
fields:
- label: Title
name: title
widget: string
- label: 'Cover Image'
name: 'image'
widget: 'image'
```
```js
media_folder: 'static/media',
public_folder: '/media',
collections: [
{
name: 'posts',
label: 'Posts',
label_singular: 'Post',
folder: 'content/posts',
path: '{{slug}}/index',
media_folder: '',
public_folder: '',
fields: [
{ label: 'Title', name: 'title', widget: 'string' },
{ label: 'Cover Image', name: 'image', widget: 'image' },
],
},
],
```
</CodeTabs>
More specifically, saving an entry with a title of `example post` with an image named `image.png` will result in a directory structure of:
```bash
content
posts
example-post
index.md
image.png
```
And for the image field being populated with a value of `image.png`.
**Note: When specifying a `path` on a folder collection, `media_folder` defaults to an empty string.**
##### Available Template Tags
Supports all of the [`slug` templates](/docs/configuration-options#slug) and:
- `{{dirname}}` The path to the file's parent directory, relative to the collection's `folder`.
- `{{filename}}` The file name without the extension part.
- `{{extension}}` The file extension.
- `{{media_folder}}` The global `media_folder`.
- `{{public_folder}}` The global `public_folder`.
### Nested Collections <img src="https://img.shields.io/badge/-Beta%20Feature-blue" alt="Beta Feature. Use at your own risk" title="Beta Feature. Use at your own risk" />
[Nested collections](/docs/beta-features/#nested-collections) is a beta feature that allows a folder collection to show a nested structure of entries and edit the locations of the entries. This feature is useful when you have a complex folder structure and may not want to create separate collections for every directory. As it is in beta, please use with discretion.
## File Collections
A `files` collection contains one or more uniquely configured files. Unlike items in `folder` collections, which repeat the same configuration over all files in the folder, each item in a `files` collection has an explicitly set path, filename, and configuration. This can be useful for unique files with a custom set of fields, like a settings file or a custom landing page with a unique content structure.
When configuring a `files` collection, configure each file in the collection separately, and list them under the `files` field of the collection. Each file has its own list of `fields` and a unique filepath specified in the `file` field (relative to the base of the repo).
**Note:** Files listed in a file collection must already exist in the hosted repository branch set in your Static CMS [backend configuration](/docs/backends-overview). Files must also have a valid value for the file type. For example, an empty file works as valid YAML, but a JSON file must have a non-empty value to be valid, such as an empty object.
<CodeTabs>
```yaml
collections:
- name: pages
label: Pages
files:
- name: about
label: About Page
file: site/content/about.yml
fields:
- name: title
label: Title
widget: string
- name: intro
label: Intro
widget: markdown
- name: team
label: Team
widget: list
fields:
- name: name
label: Name
widget: string
- name: position
label: Position
widget: string
- name: photo
label: Photo
widget: image
- name: locations
label: Locations Page
file: site/content/locations.yml
fields:
- name: title
label: Title
widget: string
- name: intro
label: Intro
widget: markdown
- name: locations
label: Locations
widget: list
fields:
- name: name
label: Name
widget: string
- name: address
label: Address
widget: string
```
```js
collections: [
{
name: 'pages',
label: 'Pages',
files: [
{
name: 'about',
label: 'About Page',
file: 'site/content/about.yml',
fields: [
{ name: 'title', label: 'Title', widget: 'string' },
{ name: 'intro', label: 'Intro', widget: 'markdown' },
{
name: 'team',
label: 'Team',
widget: 'list',
fields: [
{ name: 'name', label: 'Name', widget: 'string' },
{ name: 'position', label: 'Position', widget: 'string' },
{ name: 'photo', label: 'Photo', widget: 'image' },
],
},
],
},
{
name: 'locations',
label: 'Locations Page',
file: 'site/content/locations.yml',
fields: [
{ name: 'title', label: 'Title', widget: 'string' },
{ name: 'intro', label: 'Intro', widget: 'markdown' },
{
name: 'locations',
label: 'Locations',
widget: 'list',
fields: [
{ name: 'name', label: 'Name', widget: 'string' },
{ name: 'address', label: 'Address', widget: 'string' },
],
},
],
},
],
},
],
```
</CodeTabs>

View File

@ -0,0 +1,241 @@
---
group: Intro
title: Configuration Options
weight: 10
---
All configuration options for Static CMS are specified in a `config.yml` file, in the folder where you access the editor UI (usually in the `/admin` folder).
Alternatively, you can specify a custom config file using a link tag:
```html
<!-- Note the "type" and "rel" attribute values, which are required. -->
<link href="path/to/config.yml" type="text/yaml" rel="cms-config-url" />
```
If you prefer, you can use a javascript file (`admin/config.js`) instead of a yaml file. Simply import the javascript config and pass it into your `CMS.init({ config })` call.
To see working configuration examples, you can [start from a template](/docs/start-with-a-template) or check out the [CMS demo site](https://static-cms-demo.netlify.app). (No login required: click the login button and the CMS will open.) You can refer to the demo [configuration code](https://github.com/StaticJsCMS/static-cms/blob/main/core/dev-test/config.yml) to see how each option was configured.
You can find details about all configuration options below. Note that [YAML syntax](https://en.wikipedia.org/wiki/YAML#Basic_components) allows lists and objects to be written in block or inline style, and the code samples below include a mix of both.
## Backend
_This setting is required._
The `backend` option specifies how to access the content for your site, including authentication. Full details and code samples can be found in [Backends](/docs/backends-overview).
**Note**: no matter where you access Static CMS — whether running locally, in a staging environment, or in your published site — it will always fetch and commit files in your hosted repository (for example, on GitHub), on the branch you configured in your Static CMS config file. This means that content fetched in the admin UI will match the content in the repository, which may be different from your locally running site. It also means that content saved using the admin UI will save directly to the hosted repository, even if you're running the UI locally or in staging. If you want to have your local CMS write to a local repository, [try the local_backend setting](/docs/local-backend).
## Media and Public Folders
Static CMS users can upload files to your repository using the Media Gallery. The following settings specify where these files are saved, and where they can be accessed on your built site.
### Media Folder
_This setting is required._
The `media_folder` option specifies the folder path where uploaded files should be saved, relative to the base of the repo.
<CodeTabs>
```yaml
media_folder: "static/images/uploads"
```
```js
media_folder: 'static/images/uploads',
```
</CodeTabs>
### Public Folder
The `public_folder` option specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site. For fields controlled by \[file] or \[image] widgets, the value of the field is generated by prepending this path to the filename of the selected file. Defaults to the value of `media_folder`, with an opening `/` if one is not already included.
<CodeTabs>
```yaml
public_folder: "/images/uploads"
```
```js
public_folder: '/images/uploads',
```
</CodeTabs>
Based on the settings above, if a user used an image widget field called `avatar` to upload and select an image called `philosoraptor.png`, the image would be saved to the repository at `/static/images/uploads/philosoraptor.png`, and the `avatar` field for the file would be set to `/images/uploads/philosoraptor.png`.
This setting can be set to an absolute URL e.g. `https://netlify.com/media` should you wish, however in general this is not advisable as content should have relative paths to other content.
## Media Library
Media library integrations are configured via the `media_library` property, and its value should be an object with at least a `name` property. A `config` property can also be used for options that should be passed to the library in use.
**Example:**
<CodeTabs>
```yaml
media_library:
name: uploadcare
config:
publicKey: demopublickey
```
```js
media_library: {
name: 'uploadcare',
config: {
publicKey: 'demopublickey'
}
},
```
</CodeTabs>
## Site URL
The `site_url` setting should provide a URL to your published site. May be used by the CMS for various functionality. Used together with a collection's `preview_path` to create links to live content.
**Example:**
<CodeTabs>
```yaml
site_url: https://your-site.com
```
```js
site_url: 'https://your-site.com',
```
</CodeTabs>
## Display URL
When the `display_url` setting is specified, the CMS UI will include a link in the fixed area at the top of the page, allowing content authors to easily return to your main site. The text of the link consists of the URL with the protocol portion (e.g., `https://your-site.com`).
Defaults to `site_url`.
**Example:**
<CodeTabs>
```yaml
display_url: https://your-site.com
```
```js
display_url: 'https://your-site.com',
```
</CodeTabs>
## Custom Logo
When the `logo_url` setting is specified, the CMS UI will change the logo displayed at the top of the login page, allowing you to brand the CMS with your own logo. `logo_url` is assumed to be a URL to an image file.
**Example:**
<CodeTabs>
```yaml
logo_url: https://your-site.com/images/logo.svg
```
```js
logo_url: 'https://your-site.com/images/logo.svg',
```
</CodeTabs>
## Locale
The CMS locale. Defaults to `en`.
Other languages than English must be registered manually.
**Example**
In your `config`:
<CodeTabs>
```yaml
locale: 'de'
```
```js
locale: 'de',
```
</CodeTabs>
And in your custom JavaScript code:
```js
import CMS, { de } from '@staticcms/core';
CMS.registerLocale('de', de);
```
When a translation for the selected locale is missing the English one will be used.
> All locales are registered by default (so you only need to update your `config`).
## Search
The search functionally requires loading all collection(s) entries, which can exhaust rate limits on large repositories. It can be disabled by setting the top level `search` property to `false`.
Defaults to `true`
**Example:**
<CodeTabs>
```yaml
search: false
```
```js
search: false,
```
</CodeTabs>
## Slug Type
The `slug` option allows you to change how filenames for entries are created and sanitized. It also applies to filenames of media inserted via the default media library.\
For modifying the actual data in a slug, see the per-collection option below.
`slug` accepts multiple options:
- `encoding`
- `unicode` (default): Sanitize filenames (slugs) according to [RFC3987](https://tools.ietf.org/html/rfc3987) and the [WHATWG URL spec](https://url.spec.whatwg.org/). This spec allows non-ASCII (or non-Latin) characters to exist in URLs.
- `ascii`: Sanitize filenames (slugs) according to [RFC3986](https://tools.ietf.org/html/rfc3986). The only allowed characters are (0-9, a-z, A-Z, `_`, `-`, `~`).
- `clean_accents`: Set to `true` to remove diacritics from slug characters before sanitizing. This is often helpful when using `ascii` encoding.
- `sanitize_replacement`: The replacement string used to substitute unsafe characters, defaults to `-`.
**Example**
<CodeTabs>
```yaml
slug:
encoding: "ascii"
clean_accents: true
sanitize_replacement: "_"
```
```js
slug: {
encoding: 'ascii',
clean_accents: true,
sanitize_replacement: '_'
},
```
</CodeTabs>
## Collections
_This setting is required._
The `collections` setting is the heart of your Static CMS configuration, as it determines how content types and editor fields in the UI generate files and content in your repository. Each collection you configure displays in the left sidebar of the Content page of the editor UI, in the order they are entered into your Static CMS `config` file.
`collections` accepts a list of collection objects. See [Collections](/docs/collection-overview) for details.

View File

@ -0,0 +1,35 @@
---
group: Contributing
title: Contributor Guide
weight: 20
---
We're hoping that Static CMS will do for the [Jamstack](https://www.jamstack.org) what WordPress did for dynamic sites back in the day. We know we can't do that without building a thriving community of contributors and users, and we'd love to have you join us.
## Getting started with contributing
Being a developer is not a requirement for contributing to Static CMS, you only need the desire, a web browser, and a [GitHub account](https://github.com/join). The GitHub repo has a step-by-step [guide](https://github.com/StaticJsCMS/static-cms/blob/main/CONTRIBUTING.md) to get started with the code.
## The basics of the Static CMS docs
The documentation for Static CMS is written in [Markdown](http://daringfireball.net/projects/markdown/) (a good cheatsheet on Markdown is [here](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet)), with the source residing on [GitHub](https://github.com/StaticJsCMS/static-cms) in the `/website/content/docs` folder.
The GitHub website allows you to submit issues, work with files, search for content, and browse changes that have been submitted in the past and those that are being submitted now (aka Pull Requests).
## Style guidelines
A [style guide](/docs/writing-style-guide/) is available to help provide context around grammar, code styling, syntax, etc.
## Filing issues
If you have a GitHub account, you can file an [issue](https://github.com/StaticJsCMS/static-cms/issues) (aka bug report) against the Static CMS docs. Even if you're not able to, or don't know how to, fix the issue (see [Improve existing content](#improve-existing-content)), it helps to start the conversation.
When filing an issue, it is important to remember the [Code of Conduct](https://github.com/StaticJsCMS/static-cms/blob/main/CODE_OF_CONDUCT.md).
## Improve existing content
If you are able to offer up a change to existing content, it is welcome. Once you've forked the repo, and changed the content, you would file a pull request (PR). The repo [Contributing file](https://github.com/StaticJsCMS/static-cms/blob/main/CONTRIBUTING.md) lays out the correct format for PRs.
## Other places to get involved
Here are some links with more information about getting involved:
* [Setup instructions and Contribution Guidelines](https://github.com/StaticJsCMS/static-cms/blob/main/CONTRIBUTING.md)
* [Join our Community Chat](/chat)
* [Code of Conduct](https://github.com/StaticJsCMS/static-cms/blob/main/CODE_OF_CONDUCT.md)
* [Project Milestones](https://github.com/StaticJsCMS/static-cms/milestones)
* [Good First Issues](https://github.com/StaticJsCMS/static-cms/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22+-label%3Aclaimed)

View File

@ -0,0 +1,54 @@
---
group: Customization
title: Adding Custom Icons
weight: 100
---
The Static CMS exposes a `window.CMS` global object that you can use to register custom icons via `registerIcon`. The same object is also the default export if you import Static CMS as an npm module.
Custom icons can be used with [Collections](/docs/collection-overview) or [Custom Links & Pages](/docs/additional-links)
## Params
| Param | Type | Description |
| ----- | ------------------------------------------------------------------------------ | -------------------------------------------------- |
| name | string | A unique name for the icon |
| name | [React Function Component](https://reactjs.org/docs/components-and-props.html) | A React functional component that renders the icon |
## Example
This example uses Font Awesome to supply the icon.
```js
import { faHouse } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import CMS from '@staticcms/core';
CMS.registerIcon('house', <FontAwesomeIcon icon={faHouse} size="lg" />);
```
## Usage
### Collection
<CodeTabs>
```yaml
collections:
- name: homepage
icon: house
```
```js
collections: [
{
name: 'homepage',
icon: 'house'
},
],
```
</CodeTabs>
### Additional Links
See [Additional Links](/docs/additional-links#examples) for details.

View File

@ -0,0 +1,157 @@
---
group: Customization
title: Creating Custom Previews
weight: 50
---
The Static CMS exposes a `window.CMS` global object that you can use to register custom previews for an entire collection (or file within a file collection) via `registerPreviewTemplate`.
### React Components Inline
The `registerPreviewTemplate` requires you to provide a React component. If you have a build process in place for your project, it is possible to integrate with this build process.
However, although possible, it may be cumbersome or even impractical to add a React build phase. For this reason, Static CMS exposes some constructs globally to allow you to create components inline: `h` (alias for React.createElement) as well some basic hooks (`useState`, `useMemo`, `useEffect`, `useCallback`).
**NOTE**: `createClass` is still provided, allowing for the creation of react class components. However it has now been deprecated and will be removed in `v2.0.0`.
## Params
| Param | Type | Description |
| --------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| name | string | The name of the collection (or file for file collections) which this preview component will be used for<br /><ul><li>Folder collections: Use the name of the collection</li><li>File collections: Use the name of the file</li></ul> |
| react_component | [React Function Component](https://reactjs.org/docs/components-and-props.html) | A React functional component that renders the collection data. |
The following parameters will be passed to your `react_component` during render:
| Param | Type | Description |
| ---------- | -------------- | ---------------------------------------------------------------------------------------------------- |
| entry | object | Object with a `data` field that contains the current value of all widgets in the editor |
| document | Document | The document object the preview is within. If rendered with a frame, it will be the frame's document |
| window | Window | The window object the preview is within. If rendered with a frame, it will be the frame's window |
| getAsset | Async function | Function that given a url returns (as a promise) a loaded asset |
| widgetFor | Function | Given a field name, returns the rendered preview of that field's widget and value |
| widgetsFor | Function | Given a field name, returns the rendered previews of that field's nested child widgets and values |
### Example
```html
<script src="https://unpkg.com/@staticcms/core@%5E1.0.0/dist/static-cms-core.js"></script>
<script>
const PostPreview = ({ widgetFor, getAsset, entry }) => {
const [imageUrl, setImageUrl] = useState('');
const image = useMemo(() => entry.data.image, [entry.data.image]);
useEffect(() => {
let alive = true;
const loadImage = async () => {
const imageAsset = await getAsset(image);
if (alive) {
setImageUrl(imageAsset.toString());
}
};
loadImage();
return () => {
alive = false;
};
}, [image]);
return h(
'div',
{},
h('h1', {}, entry.data.title),
h('img', { src: imageUrl }),
h('div', { classtitle: 'text' }, widgetFor('body')),
);
});
CMS.registerPreviewTemplate('posts', PostPreview);
</script>
```
### Lists and Objects
The API for accessing the individual fields of list- and object-type entries is similar to the API for accessing fields in standard entries, but there are a few key differences. Access to these nested fields is facilitated through the `widgetsFor` function, which is passed to the preview template component during render.
**List Example:**
```html
<script>
// For list fields, the widgetFor function returns an array of objects
// that you can map over in your template. If our field is a list of
// authors containing two entries, with fields `name` and `description`,
// the return value of `widgetsFor` would look like this:
//
// [{
// data: { title: 'Mathias', description: 'Co-Founder'},
// widgets: { title: (<WidgetComponent>), description: (WidgetComponent>)}
// },
// {
// data: { title: 'Chris', description: 'Co-Founder'},
// widgets: { title: (<WidgetComponent>), description: (WidgetComponent>)}
// }]
//
// Templating would look something like this:
const AuthorsPreview = ({ widgetsFor }) => {
return h(
'div',
{},
// This is a static header that would only be rendered once for the entire list
h('h1', {}, 'Authors'),
// Here we provide a simple mapping function that will be applied to each
// object in the array of authors
widgetsFor('authors').map(function (author, index) {
return h(
'div',
{ key: index },
h('hr', {}),
h('strong', {}, author.data.name),
author.widgets.description,
);
}),
);
};
CMS.registerPreviewTemplate('authors', AuthorsPreview);
</script>
```
**Object Example:**
```html
<script>
// Object fields are simpler than lists - instead of `widgetsFor` returning
// an array of objects, it returns a single object. Accessing the shape of
// that object is the same as the shape of objects returned for list fields:
//
// {
// data: { front_limit: 0, author: 'Chris' },
// widgets: { front_limit: (<WidgetComponent>), author: (WidgetComponent>)}
// }
const GeneralPreview = ({ entry, widgetsFor }) => {
const title = entry.data.site_title;
const posts = entry.data.posts;
return h(
'div',
{},
h('h1', {}, title),
h(
'dl',
{},
h('dt', {}, 'Posts on Frontpage'),
h('dd', {}, widgetsFor('posts').widgets.front_limit || 0),
h('dt', {}, 'Default Author'),
h('dd', {}, widgetsFor('posts').data.author || 'None'),
),
);
};
CMS.registerPreviewTemplate('general', GeneralPreview);
</script>
```

View File

@ -0,0 +1,262 @@
---
group: Customization
title: Creating Custom Widgets
weight: 40
---
The Static CMS exposes a `window.CMS` global object that you can use to register custom widgets via `registerWidget`. The same object is also the default export if you import Static CMS as an npm module.
### React Components Inline
The `registerPreviewTemplate` requires you to provide a React component. If you have a build process in place for your project, it is possible to integrate with this build process.
However, although possible, it may be cumbersome or even impractical to add a React build phase. For this reason, Static CMS exposes some constructs globally to allow you to create components inline: `h` (alias for React.createElement) as well some basic hooks (`useState`, `useMemo`, `useEffect`, `useCallback`).
**NOTE**: `createClass` is still provided, allowing for the creation of react class components. However it has now been deprecated and will be removed in `v2.0.0`.
## Register Widget
Register a custom widget.
```js
// Using global window object
CMS.registerWidget(name, control, [preview], [schema]);
// Using npm module import
import CMS from '@staticcms/core';
CMS.registerWidget(name, control, [preview], [schema]);
```
### Params
| Param | Type | Description |
| ------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | Widget name, allows this widget to be used via the field `widget` property in config |
| control | [React Function Component](https://reactjs.org/docs/components-and-props.html)<br />\| string | <ul><li>`React Function Component` - The react component that renders the control. See [Control Component](#control-component)</li><li>`string` - Name of a registered widget whose control should be used (includes built in widgets).</li></ul> |
| preview | [React Function Component](https://reactjs.org/docs/components-and-props.html) | _Optional_. Renders the widget preview. See [Preview Component](#preview-component) |
| options | object | _Optional_. Widget options. See [Options](#options) |
### Control Component
The react component that renders the control. It receives the following props:
| Param | Type | Description |
| ------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------- |
| label | string | The label for the widget |
| value | An valid widget value | The current value of the widget |
| onChange | function | Function to be called when the value changes. Accepts a valid widget value |
| field | object | The field configuration for the current widget. See [Widget Options](/docs/widgets#common-widget-options) |
| collection | object | The collection configuration for the current widget. See [Collections](/docs/collection-overview) |
| config | object | The current Static CMS config. See [configuration options](/docs/configuration-options) |
| entry | object | Object with a `data` field that contains the current value of all widgets in the editor |
| path | string | `.` separated string donating the path to the current widget within the entry |
| hasErrors | boolean | Specifies if there are validation errors with the current widget |
| fieldsErrors | object | Key/value object of field names mapping to validation errors |
| isDisabled | boolean | Specifies if the widget control should be disabled |
| submitted | boolean | Specifies if a save attempt has been made in the editor session |
| forList | boolean | Specifices if the widget is within a `list` widget |
| isFieldDuplicate | function | Function that given a field configuration, returns if that field is a duplicate |
| isFieldHidden | function | Function that given a field configuration, returns if that field is hidden |
| getAsset | Async function | Function that given a url returns (as a promise) a loaded asset |
| locale | string<br />\| undefined | The current locale of the editor |
| mediaPaths | object | Key/value object of control IDs (passed to the media library) mapping to media paths |
| clearMediaControl | function | Clears a control ID's value from the internal store |
| openMediaLibrary | function | Opens the media library popup. See [Open Media Library](#open-media-library) |
| removeInsertedMedia | function | Removes draft media for a give control ID |
| removeMediaControl | function | Clears a control ID completely from the internal store |
| query | function | Runs a search on another collection. See [Query](#query) |
| i18n | object | The current i18n settings |
| t | function | Translates a given key to the current locale |
#### Open Media Library
`openMediaLibrary` allows you to open up the media library popup. It accepts the following props:
| Param | Type | Default | Description |
| ------------- | --------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
| controlID | string | | _Optional_ A unique identifier to which the uploaded media will be linked |
| forImage | boolean | `false` | _Optional_ If `true`, restricts upload to image files only |
| value | string<br />list of strings | | _Optional_ The current selected media value |
| allowMultiple | boolean | | _Optional_ Allow multiple files or images to be uploaded at once. Only used on media libraries that support multi upload |
| replaceIndex | number | | _Optional_ The index of the image in an list. Ignored if ` allowMultiple` is `false` |
| config | object | | _Optional_ Media library config options. Available options depend on the media library being used |
| field | object | | _Optional_ The current field configuration |
#### Query
`query` allows you to search the entries of a given collection. It accepts the following props:
| Param | Type | Default | Description |
| -------------- | --------------- | ------- | -------------------------------------------------------------------------------------- |
| namespace | string | | Unique identifier for search |
| collectionName | string | | The collection to be searched |
| searchFields | list of strings | | The Fields to be searched within the target collection |
| searchTerm | string | | The term to search with |
| file | string | | _Optional_ The file in a file collection to search. Ignored on folder collections |
| limit | string | | _Optional_ The number of results to return. If not specified, all results are returned |
### Preview Component
The react component that renders the preview. It receives the following props:
| Param | Type | Description |
| ---------- | --------------------- | --------------------------------------------------------------------------------------------------------- |
| value | An valid widget value | The current value of the widget |
| field | object | The field configuration for the current widget. See [Widget Options](/docs/widgets#common-widget-options) |
| collection | object | The collection configuration for the current widget. See [Collections](/docs/collection-overview) |
| config | object | The current Static CMS config. See [configuration options](/docs/configuration-options) |
| entry | object | Object with a `data` field that contains the current value of all widgets in the editor |
| getAsset | Async function | Function that given a url returns (as a promise) a loaded asset |
### Options
Register widget takes an optional object of options. These options include:
| Param | Type | Description |
| ------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| validator | function | _Optional_. Validates the value of the widget |
| getValidValue | string | _Optional_. Given the current value, returns a valid value. See [Advanced field validation](#advanced-field-validation) |
| schema | JSON Schema object | _Optional_. Enforces a schema for the widget's field configuration |
### Example
`admin/index.html`
```html
<script src="https://unpkg.com/@staticcms/core@%5E1.0.0/dist/static-cms-core.js"></script>
<script>
const CategoriesControl = ({ label, value, field, onChange }) => {
const separator = useMemo(() => field.separator ?? ', ', [field.separator]);
const handleChange = useCallback((e) => {
onChange(e.target.value.split(separator).map(e => e.trim()));
}, [separator, onChange]);
return h('div', {}, {
h('label', { for: 'inputId' }, label),
h('input', {
id: 'inputId',
type: 'text',
value: value ? value.join(separator) : '',
onChange: this.handleChange,
})
});
};
const CategoriesPreview = ({ value }) => {
return h(
'ul',
{},
value.map(function (val, index) {
return h('li', { key: index }, val);
}),
);
};
const schema = {
properties: {
separator: { type: 'string' },
},
};
CMS.registerWidget('categories', CategoriesControl, CategoriesPreview, options: { schema });
</script>
```
`admin/config.yml` (or `admin/config.js`)
<CodeTabs>
```yaml
collections:
- name: posts
label: Posts
folder: content/posts
fields:
- name: title
label: Title
widget: string
- name: categories
label: Categories
widget: categories
separator: __
```
```js
collections: [
{
name: 'posts',
label: 'Posts',
folder: 'content/posts',
fields: [
{
name: 'title'
label: 'Title'
widget: 'string'
},
{
name: 'categories'
label: 'Categories'
widget: 'categories'
separator: '__'
}
]
}
]
```
</CodeTabs>
## Advanced field validation
All widget fields, including those for built-in widgets, [include basic validation](/docs/widgets/#common-widget-options) capability using the `required` and `pattern` options.
With custom widgets, the widget can also optionally pass in a `validator` method to perform custom validations, in addition to presence and pattern. The `validator` function will be automatically called, and it can return either a `boolean` value, an `object` with a type and error message or a promise.
### Examples
#### No Errors
```javascript
const validator = () => {
// Do internal validation
return true;
};
```
#### Has Error
```javascript
const validator = () => {
// Do internal validation
return false;
};
```
#### Error With Type
```javascript
const validator = () => {
// Do internal validation
return { type: 'custom-error' };
};
```
#### Error With Type and Message
_Useful for returning custom error messages_
```javascript
const validator = () => {
// Do internal validation
return { type: 'custom-error', message: 'Your error message.' };
};
```
#### Promise
You can also return a promise from `validator`. The promise can return `boolean` value, an `object` with a type and error message or a promise.
```javascript
const validator = () => {
return this.existingPromise;
};
```

View File

@ -0,0 +1,13 @@
---
group: Customization
title: Overview
weight: 1
---
The Static CMS exposes a `window.CMS` global object that you can use to customize your CMS. The same object is also the default export if you import Static CMS as an npm module. Available options are:
- Register [custom widgets](/docs/custom-widgets)
- Register [custom previews](/docs/custom-previews)
- Register [editor customizations](/docs/widget-markdown#customization)
- Register [additional menu links or custom pages](/docs/additional-links)
- Register [custom icons](/docs/custom-icons)

View File

@ -0,0 +1,206 @@
---
group: Guides
title: Docusaurus
weight: 80
---
<Alert severity="warning">This guide is out of date and may not be currently accurate. We are working to update it. Use at your own risk.</Alert>
This guide instructs you on how to integrate Static CMS with Docusaurus.
### Before you begin
* Sign up for [GitHub](www.github.com) and [Netlify](www.netlify.com).
* Download [Node.js](https://nodejs.org/en/download/) version 14 or above.
* Install the [GitHub CLI](https://cli.github.com/).
* Install and authenticate the [Netlify CLI](https://docs.netlify.com/cli/get-started/).
## Create a new Docusaurus project
```bash
# 1. Use Docusaurus to create a site scaffold.
npx create-docusaurus@latest my-website classic
# 2. Run the development server.
cd my-website
npm run start
```
A browser window opens at `http://localhost:3000`.
The development server now serves your website at `http://localhost:3000`. As you edit the source files in `/my-website/`, you can visit `http://localhost:3000` to preview your changes.
## Push your project to GitHub
Static CMS requires a [backend](/docs/backends-overview/) to store content. Static CMS supports using Git hosts, like GitHub or GitLab, as backends. This guide uses GitHub.
```bash
# 1. Initialize your local Git repository.
git init
# 2. Rename your initial branch to match GitHub.
git branch -m main
# 3. Stage all your local files to your repository.
git add .
# 4. Commit your staged changes.
git commit -m 'Initial commit'
# 5. Create a remote repository on GitHub using the GitHub CLI.
gh repo create my-website
```
Don't add a license or a .gitignore. Do add an "origin" git remote.
![](/img/create-remote-repo.webp)
```bash
# 6. Update your remote repository with your staged changes.
git push -u origin main
```
## Publish your project using Netlify CLI
1. Connect Netlify CLI to your GitHub repository.
```bash
netlify init
```
2. Choose `Create & configure a new site`.
3. Choose your team and site name.
4. Choose `yarn build` for your build command.
5. Choose `build` for your deployment directory.
![](/img/create-remote-repo.webp)
Choose the default option for everything else.
Your website is now deployed. Netlify provides you with a randomly generated domain name. Run `netlify open --site` to view your deployed site.
## Add Static CMS to your project
### Before you begin
1. Remove all existing posts from `/blog`.
```bash
rm -rf ./blog/*
```
2. Create a new blog post post titled `2021-11-15-first-blog-post.md`.
```bash
touch ./blog/2021-11-15-first-blog-post.md
```
3. Edit `2021-11-15-first-blog-post.md` to look like this:
```yaml
---
title: First Blog Post
slug: first-blog-post
tags:
- foo
- bar
authors:
- name: Garrison McMullen
title: Instruction Writer
url: https://github.com/garrison0
image_url: https://avatars.githubusercontent.com/u/4089393?v=4
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat.
```
### Procedure
1. Create an `admin` directory inside `static`.
```bash
cd static
mkdir admin
```
2. In the `admin` directory, create a `config.yml` file and an `index.html` file.
```bash
cd admin
touch config.yml
touch index.html
```
3. Edit `index.html` to look like this:
```html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
</head>
<body>
<!-- Include the script that builds the page and powers Static CMS -->
<script src="https://unpkg.com/@staticcms/core@%5E1.0.0/dist/static-cms-core.js"></script>
</body>
</html>
```
`index.html` displays the Static CMS admin interface. You'll use the admin interface to edit your blog posts.
4. Edit `config.yml` to look like this:
```yaml
backend:
name: github
branch: main
repo: <your-github>/my-website
# These lines should *not* be indented
media_folder: "static/img" # Media files will be stored in the repo under static/images/uploads
public_folder: "/img/" # The src attribute for uploaded media will begin with /images/uploads
collections:
- name: blog
label: "blog"
folder: blog
identifier_field: title
extension: md
widget: "list"
create: true
slug: "{{year}}-{{month}}-{{day}}-{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
fields:
- { name: title, label: Title, widget: string }
- { name: body, label: Body, widget: markdown }
- { name: slug, label: Slug, widget: string }
- label: "Tags"
name: "tags"
widget: "list"
- label: "Authors"
name: "authors"
widget: "list"
fields:
- { name: name, label: Name, widget: string }
- { name: title, label: Title, widget: string }
- { name: url, label: URL, widget: string }
- { name: imageUrl, label: ImageURL, widget: string }
```
`config.yml` specifies what kind of content your blog posts have. The content specification enables Static CMS to edit existing posts and create new ones with the same format. To learn more, read about Static CMS' [](/docs/configuration-options/)[Configuration options](/docs/configuration-options/).
5. Visit `localhost:3000/admin`
You can now view and edit `2021-11-15-first-blog-post.md` through the admin interface. You can also create new blog posts.
**Warning:** Any changes you publish through the admin interface will only effect your *remote GitHub repository*. To retrieve these changes locally, `git pull` from your local repository.
6. Commit and push your new changes to your remote repository.
```bash
git add .
git commit -m "Add Static CMS"
git push
```
Netlify builds and deploys your new changes.
## Add GitHub as an authentication provider
Before you can access `/admin/` through your Netlify domain, you need to set up an authentication provider. The authentication provider allows Static CMS to determine whether users have read and write access to `/admin/`. This guide uses GitHub credentials for authentication.
### Configure GitHub
1. Create a new [GitHub OAuth application](https://github.com/settings/applications/new).
2. Enter your Netlify domain as the **Homepage URL**.
3. Enter `https://api.netlify.com/auth/done` as the **Authorization callback URL**.
4. Click **Register application.**
5. Click **Generate a new client secret.**
6. Copy the provided client secret and client ID.
### Configure Netlify
1. On Netlify, under `Site Settings > Access control > OAuth > Authentication Providers`, click **Install provider**.
2. Enter your client secret and client ID from GitHub.
3. Click **Install**.
🎉 All done! Now you can access the admin interface through your Netlify URL.

View File

@ -0,0 +1,13 @@
---
group: Contributing
title: Examples
weight: 110
---
Do you have a great, open source example? Submit a pull request to this page!
<div class="non-props-table" />
| Example | Tools | Type | Source | More info |
| ---------------------------------------------------------------- | ----------- | ------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| [St Joseph Catholic Church](https://stjosephchurchbluffton.org/) | Next, React | Website | [saint-joseph-catholic-church-site](https://github.com/SaintJosephCatholicChurch/saint-joseph-catholic-church-site) | [README](https://github.com/SaintJosephCatholicChurch/saint-joseph-catholic-church-site/blob/main/README.md) |

View File

@ -0,0 +1,140 @@
---
group: Guides
title: Gatsby
weight: 10
---
<Alert severity="warning">This guide is out of date and may not be currently accurate. We are working to update it. Use at your own risk.</Alert>
This guide will help you get started using Static CMS and Gatsby.
To get up and running with Gatsby, you'll need to have [Node.js](https://nodejs.org/) installed on your computer. _Note: Gatsby's minimum supported Node.js version is Node 8._
## Create a new Gatsby site
Let's create a new site using the default Gatsby Starter Blog. Run the following commands in the terminal, in the folder where you'd like to create the blog:
```bash
npm install -g gatsby-cli
gatsby new blog https://github.com/gatsbyjs/gatsby-starter-blog
cd blog
```
## Get to know Gatsby
In your favorite code editor, open up the code generated for your "Gatsby Starter Blog" site, and take a look at the `content` directory.
You will see that there are multiple Markdown files that represent blog posts. Open one `.md` file and you will see something like this:
```yml
---
title: New Beginnings
date: '2015-05-28T22:40:32.169Z'
description: This is an optional description for SEO and Open Graph purposes, rather than the default generated excerpt.
---
Far far away, behind the word mountains, far from the countries Vokalia and
Consonantia, there live the blind texts.
```
We can see above that each blog post has a title, a date, a description and a body. Now, let's recreate this using Static CMS.
## Add Static CMS to your site
First let's install some dependencies. We'll need `@staticcms/core` and `gatsby-plugin-netlify-cms`. Run the following command in the terminal at the root of your site:
```bash
npm install --save @staticcms/core gatsby-plugin-netlify-cms
```
### Configuration
For the purpose of this guide we will deploy to Netlify from a GitHub repository which requires the minimum configuration.
Create a `config.yml` file in the directory structure you see below:
```bash
├── static
│ ├── admin
│ │ ├── config.yml
```
In your `config.yml` file paste the following configuration:
```yml
backend:
name: git-gateway
branch: main # Branch to update (optional; defaults to main)
media_folder: static/img
public_folder: /img
collections:
- name: 'blog'
label: 'Blog'
folder: 'content/blog'
create: true
slug: 'index'
media_folder: ''
public_folder: ''
path: '{{title}}/index'
editor:
preview: false
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- { label: 'Description', name: 'description', widget: 'string' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
**Note:** The above configuration allows assets to be stored relative to their content. Therefore posts would be stored in the format below as it is in `gatsby-starter-blog`.
```bash
content/
├── blog
│ ├── first-post-title
│ │ ├── index.md
│ │ └── post-image.jpg
└── └── second-post-title
├── index.md
└── post-image.jpg
```
Finally, add the plugin to your `gatsby-config.js`.
```javascript
plugins: [`gatsby-plugin-netlify-cms`];
```
### Push to GitHub
It's now time to commit your changes and push to GitHub. The Gatsby starter initializes Git automatically for you, so you only need to do:
```bash
git add .
git commit -m "Initial Commit"
git remote add origin https://github.com/YOUR_USERNAME/NEW_REPO_NAME.git
git push -u origin main
```
### Add your repo to Netlify
Go to Netlify and select 'New Site from Git'. Select GitHub and the repository you just pushed to. Click Configure Netlify on GitHub and give access to your repository. Finish the setup by clicking Deploy Site. Netlify will begin reading your repository and starting building your project.
### Enable Identity and Git Gateway
Netlify's Identity and Git Gateway services allow you to manage CMS admin users for your site without requiring them to have an account with your Git host or commit access on your repo. From your site dashboard on Netlify:
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you want only invited users to access your CMS, but if you're just experimenting, you can leave it open for convenience.
3. If you'd like to allow one-click login with services like Google and GitHub, check the boxes next to the services you'd like to use, under **External providers**.
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This authenticates with your Git host and generates an API access token. In this case, we're leaving the **Roles** field blank, which means any logged in user may access the CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
## Start publishing
It's time to create your first blog post. Login to your site's `/admin/` page and create a new post by clicking New Blog. Add a title, a date and some text. When you click Publish, a new commit will be created in your GitHub repo with this format `Create Blog "year-month-date-title"`.
Then Netlify will detect that there was a commit in your repo, and will start rebuilding your project. When your project is deployed you'll be able to see the post you created.
### Cleanup
It is now safe to remove the default Gatsby blog posts.

View File

@ -0,0 +1,42 @@
---
group: Backends
title: Git Gateway
weight: 10
---
- **Name**: `git-gateway`
[Git Gateway](https://github.com/netlify/git-gateway) is a Netlify open source project that allows you to add content editors to your site CMS without giving them direct write access to your GitHub or GitLab repository. (For Bitbucket repositories, use the [Bitbucket backend](/docs/bitbucket-backend/) instead.)
## Git Gateway with Netlify
The [Netlify Identity](https://www.netlify.com/docs/identity/) service can handle the authentication and provides a simple interface for user management. The Static CMS [featured templates](/docs/start-with-a-template) are working examples of this backend.
To use it in your own project stored on GitHub or GitLab, follow these steps:
1. Head over to the [Netlify Identity docs](https://www.netlify.com/docs/identity) and follow the steps to get started.
2. Add the following lines to your Static CMS `config` file:
<CodeTabs>
```yaml
backend:
name: git-gateway
```
```js
backend: {
name: 'git-gateway',
},
```
</CodeTabs>
## Reconnect after Changing Repository Permissions
If you change ownership on your repository, or convert a repository from public to private, you may need to reconnect Git Gateway with proper permissions. Find further instructions in the [Netlify Git Gateway docs](https://www.netlify.com/docs/git-gateway/#reconnect-after-changing-repository-permissions).
## Git Gateway without Netlify
You can use [Git Gateway](https://github.com/netlify/git-gateway) without Netlify by setting up your own Git Gateway server and connecting it with your own instance of [GoTrue](https://www.gotrueapi.org) (the open source microservice that powers Netlify Identity), or with any other identity service that can issue JSON Web Tokens (JWT).
To configure in Static CMS, use the same `backend` settings in your Static CMS `config` file as described in Step 2 of the [Git Gateway with Netlify Identity](#git-gateway-with-netlify-identity) instructions above.

View File

@ -0,0 +1,42 @@
---
title: GitHub
group: Backends
weight: 20
---
- **Name**: `github`
For repositories stored on GitHub, the `github` backend allows CMS users to log in directly with their GitHub account. Note that all users must have push access to your content repository for this to work.
## Authentication
Because Github requires a server for authentication, Netlify facilitates basic GitHub authentication.
To enable basic GitHub authentication:
1. Follow the authentication provider setup steps in the [Netlify docs](https://www.netlify.com/docs/authentication-providers/#using-an-authentication-provider).
2. Add the following lines to your Static CMS `config` file:
<CodeTabs>
```yaml
backend:
name: github
repo: owner-name/repo-name # Path to your GitHub repository
# optional, defaults to main
# branch: main
```
```js
backend: {
name: 'github',
repo: 'owner-name/repo-name', // Path to your GitHub repository
// optional, defaults to main
// branch: 'main'
},
```
</CodeTabs>
## Git Large File Storage (LFS)
Please note that the GitHub backend **does not** support [git-lfs](https://git-lfs.github.com/).

View File

@ -0,0 +1,81 @@
---
group: Backends
title: GitLab
weight: 40
---
- **Name**: `gitlab`
For repositories stored on GitLab, the `gitlab` backend allows CMS users to log in directly with their GitLab account. Note that all users must have push access to your content repository for this to work.
**Note:** GitLab default branch is protected by default, thus typically requires `maintainer` permissions in order for users to have push access.
## Authentication
With GitLab's PKCE authorization, users can authenticate with GitLab directly from the client. To do this:
1. Follow the [GitLab docs](https://docs.gitlab.com/ee/integration/oauth_provider.html#adding-an-application-through-the-profile) to add your Static CMS instance as an OAuth application and uncheck the **Confidential** checkbox. For the **Redirect URI**, enter the address where you access Static CMS, for example, `https://www.mysite.com/admin/`. For scope, select `api`.
2. GitLab gives you an **Application ID**. Copy this ID and enter it in your Static CMS `config` file, along with the following settings:
| Name | Type | Default | Description |
| --------- | ------ | ------- | ---------------------------------------- |
| auth_type | 'pkce' | | The authorization method |
| app_id | string | | Application ID from your GitLab settings |
### Example
<CodeTabs>
```yaml
backend:
name: gitlab
repo: owner-name/repo-name # Path to your GitLab repository
auth_type: pkce # Required for pkce
app_id: your-app-id # Application ID from your GitLab settings
```
```js
backend: {
name: 'gitlab',
repo: 'owner-name/repo-name', // Path to your GitLab repository
auth_type: 'pkce', // Required for pkce
app_id: 'your-app-id', // Application ID from your GitLab settings
},
```
</CodeTabs>
### Self-Hosted GitLab Instance
You can also use PKCE Authorization with a self-hosted GitLab instance. This requires adding `api_root`, `base_url`, and `auth_endpoint` fields:
| Name | Type | Default | Description |
| ------------- | ------ | ------- | ------------------------------------- |
| api_root | string | | Root API url for self-hosted instance |
| base_url | string | | Root url for self-hosted instance |
| auth_endpoint | string | | Auth endpoint on self-hosted instance |
#### Example
<CodeTabs>
```yaml
backend:
name: gitlab
repo: owner-name/repo-name # Path to your GitLab repository
auth_type: pkce # Required for pkce
app_id: your-app-id # Application ID from your GitLab settings
api_root: https://my-hosted-gitlab-instance.com/api/v4
base_url: https://my-hosted-gitlab-instance.com
auth_endpoint: oauth/authorize
```
```js
backend: {
name: 'gitlab',
repo: 'owner-name/repo-name', // Path to your GitLab repository
auth_type: 'pkce', // Required for pkce
app_id: 'your-app-id', // Application ID from your GitLab settings
api_root: 'https://my-hosted-gitlab-instance.com/api/v4',
base_url: 'https://my-hosted-gitlab-instance.com',
auth_endpoint: 'oauth/authorize',
},
```
</CodeTabs>

View File

@ -0,0 +1,162 @@
---
group: Guides
title: Gridsome
weight: 70
---
<Alert severity="warning">This guide is out of date and may not be currently accurate. We are working to update it. Use at your own risk.</Alert>
This guide will help you get started using Static CMS and Gridsome.
## How to install Gridsome
### 1. Install Gridsome CLI tool
```bash
# Using Yarn
yarn global add @gridsome/cli
# Using NPM
npm install --global @gridsome/cli
```
## Create a new Gridsome website
```bash
# To create a new project run
gridsome create gridsome-netlify-blog
# Then navigate to the project folder
cd gridsome-netlify-blog
# To start local dev server at http://localhost:8080
gridsome develop
```
### Install Static CMS the required dependencies to your project
```bash
# Using Yarn
yarn add @staticcms/core gridsome-plugin-netlify-cms @gridsome/source-filesystem @gridsome/transformer-remark
# Using NPM
npm add @staticcms/core gridsome-plugin-netlify-cms @gridsome/source-filesystem @gridsome/transformer-remark
```
Now that the plugins are installed, it's time to setup the configuration. Open the `gridsome.config.js` file and update its content to:
```js
module.exports = {
sitetitle: 'Gridsome',
transformers: {
remark: {
externalLinksTarget: '_blank',
externalLinksRel: ['nofollow', 'noopener', 'noreferrer'],
anchorClasstitle: 'icon icon-link'
}
},
plugins: [
{
use: '@gridsome/source-filesystem',
options: {
path: 'posts/**/*.md',
typetitle: 'Post'
}
},
{
use: `gridsome-plugin-netlify-cms`,
options: {
publicPath: `/admin`
}
},
]
}
```
Please read [gridsome-plugin-netlify-cms](https://gridsome.org/plugins/gridsome-plugin-netlify-cms), [transformer-remark](https://gridsome.org/plugins/@gridsome/transformer-remark) for more information.
## Static CMS setup
1. Create an `admin` directory inside the `src`
2. Create an `uploads` directory inside the root of your project
3. Add `index.html`, `index.js` and a `config.yml` file to your `admin` directory
Your `index.html` should look like this:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Static CMS</title>
</head>
<body>
<script src="index.js" type="module"></script>
</body>
</html>
```
Your `index.js` should look like this:
```js
import CMS from "@staticcms/core"
```
Your `config.yml` for GitHub should look like this:
```yml
backend:
name: git-gateway
branch: main # Branch to update (optional; defaults to main)
media_folder: "static/uploads"
public_folder: "/uploads"
collections:
- name: "posts"
label: "Posts"
folder: "posts"
create: true
slug: "{{slug}}"
fields:
- {label: "Title", name: "title", widget: "string"}
- {label: "Excerpt", name: "excerpt", widget: "string"}
- {label: "Publish Date", name: "date", widget: "datetime"}
- {label: "Body", name: "body", widget: "markdown"}
```
## Push to GitHub
It's now time to commit your changes and push to GitHub.
```bash
git init
git add .
git commit -m "Initial Commit"
git remote add origin https://github.com/YOUR_USERNAME/NEW_REPO_NAME.git
git push -u origin main
```
### Add your repo to Netlify
Go to Netlify and select 'New Site from Git'. Select GitHub and the repository you just pushed to. Click Configure Netlify on GitHub and give access to your repository. Finish the setup by clicking Deploy Site. Netlify will begin reading your repository and starting building your project.
### Enable Identity and Git Gateway
Netlify's Identity and Git Gateway services allow you to manage CMS admin users for your site without requiring them to have an account with your Git host or commit access on your repo. From your site dashboard on Netlify:
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you want only invited users to access your CMS, but if you're just experimenting, you can leave it open for convenience.
3. If you'd like to allow one-click login with services like Google and GitHub, check the boxes next to the services you'd like to use, under **External providers**.
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This authenticates with your Git host and generates an API access token. In this case, we're leaving the **Roles** field blank, which means any logged in user may access the CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
## Start publishing
It's time to create your first blog post. Login to your site's `/admin/` page and create a new post by clicking New Blog. Add a title, a date and some text. When you click Publish, a new commit will be created in your GitHub repo with this format `Create Blog "year-month-date-title"`.
Then Netlify will detect that there was a commit in your repo, and will start rebuilding your project. When your project is deployed you'll be able to see the post you created.
Your basic blog scaffold is done, now you can query data from the GraphQL server just like you're working with the filesystem. For more info read [querying data](https://gridsome.org/docs/querying-data).

202
docs/content/docs/hugo.mdx Normal file
View File

@ -0,0 +1,202 @@
---
group: Guides
title: Hugo
weight: 20
---
<Alert severity="warning">This guide is out of date and may not be currently accurate. We are working to update it. Use at your own risk.</Alert>
This guide will walk you through how to integrate Static CMS with Hugo. This is a good place to start if you want to learn from the ground up how these two tools work together. If you want to get up-and-running quicker, you can use one of the pre-existing and amazing [starter templates](/docs/start-with-a-template/)!
## Getting started with Hugo
### Installation
To get started with Hugo, you should first install the command line tool. If you've already got it installed, you can [skip this step](#creating-a-new-site). On MacOS and Linux you can do this with:
```bash
brew install hugo
```
To test that it's successfully installed, you can try this command, to get a list of Hugo's options:
```bash
hugo help
```
### Creating a new site
Create a new Hugo project and start it up using the following commands.
```bash
hugo new site <name-of-your-new-project>
cd <name-of-your-new-project>
hugo server
```
You won't actually see anything, just yet, and that's because you don't have any template files. That's easily resolved. In the `layouts/` directory, create a file `index.html` and put a basic HTML structure in there:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<h1>Nice. It's looking good already.</h1>
</body>
</html>
```
You'll also add some files to the `content/` and `data/` directories to make sure git tracks them.
```bash
touch content/.keep data/.keep
```
This is as basic as you can get with a Hugo project. There's just enough here now for us to install Static CMS.
## Getting Started With Static CMS
### Add the Static CMS files to Hugo
In Hugo, static files that don't need to be processed by the build commands live in the `static/` directory. You'll install the Static CMS admin and config files there. Create a directory `admin/` and within it, create two files `index.html` and `config.yml`. In the `index.html`, add the following content:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
<!-- Include the script that enables Netlify Identity on this page. -->
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
</head>
<body>
<!-- Include the script that builds the page and powers Static CMS -->
<script src="https://unpkg.com/@staticcms/core@%5E1.0.0/dist/static-cms-core.js"></script>
</body>
</html>
```
In the `config.yml` file, you can add this basic configuration — you can customize as you see fit, this sample file is just to get you started.
```yaml
backend:
name: git-gateway
branch: main # Branch to update (optional; defaults to main)
media_folder: static/img
public_folder: /img
collections:
- name: 'blog'
label: 'Blog'
folder: 'content/blog'
create: true
slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
editor:
preview: false
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- { label: 'Description', name: 'description', widget: 'string' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
**Note:** You won't be able to access the CMS just yet — you still need to deploy the project with **Netlify** and authenticate with **Netlify Identity**. You'll handle this in the next few steps of this guide.
### Pushing to GitHub
It's now time to commit your changes and push to GitHub. You can run the following commands to initialize a git repository and push the changes so far.
```bash
git init # Initialize a git repository
git add . # Add every file
git commit -m "Initial Commit" # Commit every file with the message 'Initial Commit'
git remote add origin https://github.com/YOUR_USERNAME/NEW_REPO_NAME.git # Create a new repo on GitHub and add it to this project as a remote repository.
git push -u origin main # Push your changes
```
### Deploying With Netlify
Now you can go ahead and deploy to Netlify. Go to your Netlify dashboard and click **[New site from Git](https://app.netlify.com/start)**. Select the repo you just created. Under **Basic build settings**, you can set the build command to `hugo` and the publish directory to `public`. Click **Deploy site** to get the process going.
### Authenticating with Netlify Identity
**Add the Netlify Identity Widget**
You've already added the Netlify Identity widget to our `admin/index.html`. The next thing to do is add the Netlify Identity widget to our site's index page. In `layouts/index.html`, we can add the following to the `<head>` tag on the page:
```html
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
```
Once you've added this, make sure to push your changes to GitHub!
### Enable Identity & Git Gateway in Netlify
Back in your [Netlify dashboard](https://app.netlify.com/):
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Once enabled, select **Settings and usage**, and scroll down to **Registration preferences**. You can set this to either **Open** or **Invite only**, but usually **Invite only** is your best bet for a personal site.
3. If you don't want to create an account, or would like to use an external provider such as GitHub or Google, you can enable those services under **External providers**.
4. Scroll down to **Services** and click **Enable Git Gateway**.
### Accessing the CMS
Once you've reached this point, you should be able to access the CMS in your browser at `http://localhost:1313/admin`. You'll be prompted to add the URL of your Netlify site. Once you've added that URL, you can log in with an Identity account or with one of the External Providers you enabled in step 3 above. For the sake of this tutorial, you can create a blog post in the CMS, and publish it! Once you `git pull` in your project, the blog post will show up in the project at `content/blog/<slugified-blog-post-title>.md`.
And that's it! From this point on, it's just a matter of following [the Hugo documentation](https://gohugo.io/templates/) for outputting the content from your `content/` directory into templates! For more information on configuring Static CMS, feel free to check out the [Static CMS configuration options documentation](/docs/configuration-options/).
## Using Static CMS content in Hugo
### Creating a list of posts
In your `layouts/index.html` file, you'll create an unordered list element and use a Hugo `range` to output all posts. Inside that range, you can add a list item element with each post title and a link to the post inside it.
**Note:** To learn more about Hugo's `range` function, check out [the Hugo documentation](https://gohugo.io/functions/range).
```html
<body>
<h1>Nice. It's looking good already.</h1>
<ul>
{{ range (where .Pages "Section" "blog") }}
<li>
<a href="{{ .RelPermalink }}">
{{ .Title }}
</a>
</li>
{{ end }}
</ul>
</body>
```
That link won't work just right just yet. You'll need to make a single page layout for blog posts, so Hugo can create a page for the `.RelPermalink` to link to.
### Creating a single page post layout
Create a file `layouts/blog/single.html`, and put the following content in there:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>{{ .Title }}</title>
</head>
<body>
<h1>{{ .Title }}</h1>
<p class="date">{{ .Date }}</p>
<p class="description">{{ .Params.description }}</p>
<article class="content">
{{ .Content }}
</article>
</body>
</html>
```
You can see this basic template includes all the fields you've specified in your Static CMS `config.yml` file. You can access any custom front-matter fields with `.Params.<field-name>`!

View File

@ -0,0 +1,24 @@
---
group: Intro
title: Overview
weight: 1
---
Static CMS is an open source content management system for your Git workflow that enables you to provide editors with a friendly UI. You can use it with any static site generator to create faster, more flexible web projects. Content is stored in your Git repository alongside your code for easier versioning and the option to handle content updates directly in Git.
At its core, Static CMS is an open-source React app that acts as a wrapper for the Git workflow, using the GitHub, GitLab, or Bitbucket APIs. This provides many advantages, including:
- **Fast, web-based UI:** With rich-text editing, real-time preview, and drag-and-drop media uploads.
- **Platform agnostic:** Works with most static site generators.
- **Easy installation:** Add two files to your site, hook up the backend by including those files in your build process or linking to our Content Delivery Network (CDN) and call `CMS.init();`.
- **Modern authentication:** Using GitHub, GitLab, or Bitbucket and JSON web tokens.
- **Flexible content types:** Specify an unlimited number of content types with custom fields.
- **Fully extensible:** Create custom-styled previews, UI widgets, editor plugins, pages, etc.
## Find out more
- Get a feel for the UI in the [demo site](https://static-cms-demo.netlify.app). (No login required. Click the login button to go straight to the CMS editor UI.)
- [Start with a template](/docs/start-with-a-template/) to make a Static CMS-enabled site of your own.
- Configure your existing site by following a [tutorial](/docs/add-to-your-site/) or checking [configuration options](/docs/configuration-options).
- Ask questions and share ideas in the Static CMS [community chat](/chat).
- Get involved in new developments and become a [contributor](/docs/contributor-guide/).

View File

@ -0,0 +1,300 @@
---
group: Guides
title: Jekyll
weight: 30
---
<Alert severity="warning">This guide is out of date and may not be currently accurate. We are working to update it. Use at your own risk.</Alert>
This section will help you integrate Static CMS with a new or existing Jekyll project.
[Jekyll](https://jekyllrb.com/) is a blog-aware static site generator built with Ruby. [Github Pages](https://pages.github.com/) are powered by Jekyll, making it a popular choice for developer blogs and project pages.
If you're starting a new project, the fastest route to publishing on a Jekyll website with Static CMS is to [deploy a template on Netlify](https://templates.netlify.com/).
## Setup
This guide will use the blog you get if you follow the [really excellent official Jekyll step by step tutorial](https://jekyllrb.com/docs/step-by-step/01-setup/) as a starting point. If you're new to Jekyll - I recommended you start by following the tutorial so you know your way around your new blog. Otherwise [you can clone this repo](https://github.com/adamwatters/jekyll-tutorial-with-netlify-cms/tree/without-cms) and checkout the `without-cms` branch.
![Jekyll tutorial blog screenshot](/img/screenshot-jekyll-tutorial-blog.webp)
## Add Static CMS
### Add admin/index.html
Create a file `admin/index.html` in the root of your repo - it should look like this:
```html
<!-- admin/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>
<!-- Include the identity widget -->
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js" type="text/javascript"></script>
</head>
<body>
<!-- Include the script that builds the page and powers Static CMS -->
<script src="https://unpkg.com/@staticcms/core@%5E1.0.0/dist/static-cms-core.js"></script>
</body>
</html>
```
### Add admin/config.yml
Create a file `admin/config.yml` in the root of your repo - it should look like this:
```yml
# config.yml
backend:
name: git-gateway
branch: main # Branch to update (optional; defaults to main)
media_folder: 'assets/uploads'
collections:
- name: 'blog'
label: 'Blog'
folder: '_posts/'
fields:
- { name: Title }
```
### Enable authentication for CMS users
Static CMS stores content in your online Git repository. Therefore, to make content changes, users need to authenticate with the corresponding Git provider to prove that they have read and write access to that content.
Follow the directions in the Introduction section to [enable Netlify Identity and Git Gateway services](/docs/add-to-your-site/#enable-identity-and-git-gateway) for the backend, then [add the Identity widget](/docs/add-to-your-site/#add-the-netlify-identity-widget) to render a login portal on the frontend.
## CMS Configuration
### Blog Collection
We'll start by updating the `blog` collection. Blogging is baked into Jekyll, and the `_posts/` directory uses [some special conventions](https://jekyllrb.com/docs/posts/) we'll need to keep in mind as we configure Static CMS. Copy and paste the following into your `config.yml`.
```yaml
collections:
- name: 'blog'
label: 'Blog'
folder: '_posts/'
create: true
slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
editor:
preview: false
fields:
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'post' }
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
A few things to note.
* We set the `slug` to `'{{year}}-{{month}}-{{day}}-{{slug}}'` because [Jekyll requires this format for blog posts](https://jekyllrb.com/docs/posts/#creating-posts). `year`, `month`, and `day` will be extracted from the `date` field, and `slug` will be generated from the `title` field.
* We added `editor` configuration with a field `preview: false`. This will eliminate the preview pane. Because Jekyll uses Liquid templates, there currently isn't a good way to provide a preview of pages as you update the content.
* The `layout` field default is set to `post` so Jekyll knows to use `_layouts/post.html` when it renders a post. This field is hidden because we want all posts to use the same layout.
* The `date` and `title` field will be used by the `slug` - as noted above, Jekyll relies on the filename to determine a post's publish date, but Static CMS does not pull date information from the filename and requires a frontmatter `date` field. **Note** Changing the `date` or `title` fields in Static CMS will not update the filename. This has a few implications:
* If you change the `date` or `title` fields in Static CMS, Jekyll won't notice
* You don't necessarily need to change the `date` and `title` fields for existing posts, but if you don't the filenames and frontmatter will disagree in a way that might be confusing
* If you want to avoid these issues, use a regular Jekyll collection instead of the special `_posts` directory
### Author Collection
In addition to `_posts`, the Jekyll tutorial blog includes a collection of authors in the `_authors` directory. Before we can configure Static CMS to work with the `authors` collection, we'll need to make a couple tweaks to our Jekyll blog. Here's the front matter for one of the authors.
```yaml
short_title: jill
title: Jill Smith
position: Chief Editor
```
`name` has special meaning as a unique identifier in Static CMS, but as set up now our Jekyll blog is using `short_name` as the unique identifier for authors. For each author, update the frontmatter like so.
```yaml
title: jill
display_title: Jill Smith
position: Chief Editor
```
then update `_layouts/author.html`, `_layouts/post.html` and `staff.html` accordingly.
```html
<!-- _layouts/author.html -->
--- layout: default ---
<h1>{{ page.display_name }}</h1>
<h2>{{ page.position }}</h2>
{{ content }}
<h2>Posts</h2>
<ul>
{% assign filtered_posts = site.posts | where: 'author', page.name %} {% for post in
filtered_posts %}
<li>
<a href="{{ site.baseurl }}{{ post.url }}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
```
```html
<!-- _layouts/post.html -->
--- layout: default ---
<h1>{{ page.title }}</h1>
<p>
{{ page.date | date_to_string }} {% assign author = site.authors | where: 'name', page.author |
first %} {% if author %} - <a href="{{ author.url }}">{{ author.display_name }}</a>
{% endif %}
</p>
{{ content }}
```
```html
<!-- staff.html -->
--- layout: default ---
<h1>Staff</h1>
<ul>
{% for author in site.authors %}
<li>
<h2>
<a href="{{ site.baseurl }}{{ author.url }}">{{ author.display_name }}</a>
</h2>
<h3>{{ author.position }}</h3>
<p>{{ author.content | markdownify }}</p>
</li>
{% endfor %}
</ul>
```
Next, copy and paste the following into the collections array in `config.yml` below the `blog` collection.
```yaml
- name: 'authors'
label: 'Authors'
folder: '_authors/'
create: true
editor:
preview: false
fields:
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'author' }
- { label: 'Short Name', name: 'name', widget: 'string' }
- { label: 'Display Name', name: 'display_name', widget: 'string' }
- { label: 'Position', name: 'position', widget: 'string' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
Now that we have the `authors` collection configured, we can add an `author` field to the `blog` collection. We'll use the [relation widget](/docs/widgets/#relation) to define the relationship between blog posts and authors.
```yaml
# updated fields in blog collection configuration
fields:
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'post' }
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- {
label: 'Author',
name: 'author',
widget: 'relation',
collection: 'authors',
display_fields: [display_name],
search_fields: [display_name],
value_field: 'name',
}
- { label: 'Body', name: 'body', widget: 'markdown' }
```
With that configuration added, you should be able to select the author for a post from a dropdown.
### About Page
Our Jekyll blog includes an About page. It would nice to be able to edit that page just like we can edit our blog and author pages. Static CMS provides [file collections](/docs/collection-types/#file-collections) to solve this problem.
Copy and paste the following into the collections array in `config.yml`
```yaml
- name: 'pages'
label: 'Pages'
editor:
preview: false
files:
- label: 'About Page'
name: 'about'
file: 'about.md'
fields:
- { label: 'Title', name: 'title', widget: 'hidden', default: 'about' }
- { label: 'Layout', name: 'layout', widget: 'hidden', default: 'about' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
### Navigation
The last aspect of our Jekyll blog we might want to bring under the control of Static CMS is our Navigation menu. Our Jekyll tutorial blog has a file `_data/navigation.yml` that defines the links rendered by `_includes/navigation.html`. It looks like this.
```yaml
# _data/navigation.yml
- title: Home
link: /
- title: About
link: /about.html
- title: Blog
link: /blog.html
- title: Staff
link: /staff.html
```
To make this file editable with Static CMS, we'll need to make one minor tweak. The issue is this file contains a yaml array at the top level, but Static CMS is designed to work with yaml objects. Update `_data/navigation.yml` so it looks like so.
```yaml
# _data/navigation.yml
items:
- title: Home
link: /
- title: About
link: /about.html
- title: Blog
link: /blog.html
- title: Staff
link: /staff.html
```
You'll need to update `_includes/navigation.html` accordingly. `{% for item in site.data.navigation %}` should be changed to `{% for item in site.data.navigation.items %}`. When you're done, the nav html should look like this.
```html
<nav>
{% for item in site.data.navigation.items %}
<a href="{{ site.baseurl }}{{ item.link }}" {% if page.url == item.link %}style="color: red;"{% endif %}>
{{ item.name }}
</a>
{% endfor %}
</nav>
```
Finally, add the following to the collections array in `config.yml`
```yaml
- name: 'config'
label: 'Config'
editor:
preview: false
files:
- label: 'Navigation'
name: 'navigation'
file: '_data/navigation.yml'
fields:
- label: 'Navigation Items'
name: 'items'
widget: 'list'
fields:
- { label: Name, name: name, widget: string }
- { label: Link, name: link, widget: string }
```
Now you can add, rename, and rearrange the navigation items on your blog.

View File

@ -0,0 +1,84 @@
---
group: Backends
title: Local Backend
weight: 50
---
The local backend allows you to use Static CMS with a local git repository, instead of working with a live repo, regardless of backend provider. It will read and write file from your local file system inside your local git repository. You will still need to manually commit and push any files you have changed or added after completing the edits.
## Configuration
| Name | Type | Default | Description |
| ------------- | --------------------------------------------- | ------- | ----------------------------------------------------------------------------------- |
| local_backend | boolean<br />\| [Proxy Config](#configure-proxy-server-port) | `false` | Activates the local backend for Static CMS, overriding other backend configurations |
### Example
<CodeTabs>
```yaml
backend:
name: git-gateway
# when using the default proxy server port
local_backend: true
```
```js
backend: {
name: 'git-gateway',
},
// when using the default proxy server port
local_backend: true,
```
</CodeTabs>
## Usage
1. Run `npx @staticcms/proxy-server` from the root directory of the above repository.
- If the default port (8081) is in use, the proxy server won't start and you will see an error message. In this case, follow [these steps](#configure-the-@staticcms/proxy-server-port-number) before proceeding.
2. Start your local development server (e.g. run `gatsby develop`).
3. Open `http://localhost:<port>/admin` to verify that your can administer your content locally. Replace `<port>` with the port of your local development server. For example Gatsby's default port is `8000`
**Note:** `@staticcms/proxy-server` runs an unauthenticated express server. As any client can send requests to the server, it should only be used for local development.
### Configure Proxy Server Port
| Name | Type | Default | Description |
| ------------- | ------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| url | string | `http://localhost:8081/api/v1` | URL for proxy server |
| allowed_hosts | list of hosts | `['localhost', '127.0.0.1']` | Whitelist of allowed hosts when accessing the local site from a host other than 'localhost' or '127.0.0.1' |
1. Create a `.env` file in the project's root folder and define the PORT you'd like the proxy server to use.
```ini
PORT=8082
```
2. Update the `local_backend` object in `config` and specify a `url` property to use your custom port number
<CodeTabs>
```yaml
backend:
name: git-gateway
local_backend:
# when using a custom proxy server port
url: http://localhost:8082/api/v1
# when accessing the local site from a host other than 'localhost' or '127.0.0.1'
allowed_hosts: ['192.168.0.1']
```
```js
backend: {
name: 'git-gateway',
},
local_backend: {
// when using a custom proxy server port
url: 'http://localhost:8082/api/v1',
// when accessing the local site from a host other than 'localhost' or '127.0.0.1'
allowed_hosts: ['192.168.0.1'],
},
```
</CodeTabs>

View File

@ -0,0 +1,183 @@
---
group: Guides
title: Middleman
weight: 60
---
<Alert severity="warning">This guide is out of date and may not be currently accurate. We are working to update it. Use at your own risk.</Alert>
This guide will help you get started using Static CMS and Middleman.
## Installation
To get up and running with Middleman, you need both the Ruby language runtime and RubyGems installed on your computer. Check out the [Middleman installation docs](https://middlemanapp.com/basics/install/) for more details. If you already have your environment set up, use the following command to install Middleman:
```bash
gem install middleman
```
## Create a new Middleman site
Let's create a new site from scratch. Run the following commands in the terminal, in the folder where you'd like to create the blog:
```bash
middleman init blog
cd blog
```
### Add the Middleman blog extension
Middleman has an official extension to support blogging, articles and tagging. `middleman-blog` ships as an extension and must be installed to use. Simply specify the gem in your Gemfile:
```bash
gem "middleman-blog"
```
Install the dependencies and run Middleman with the following commands:
```bash
bundle install
middleman server
```
## Get started with Middleman
Now we have our site up and running let's open up a code editor and create a new folder `source/posts` and add your first article named `2019-01-01-example-article.html.md` with the following content:
```yml
---
title: Example Article
date: 2019-01-01
---
This is an example article. You probably want to delete it and write your own articles once you finished this guide!
```
### Activate the blog extension
We can then activate the blog in `config.rb`. Be sure to check out the [Middleman blogging docs](https://middlemanapp.com/basics/blogging/) for all the configuration options.
```bash
activate :blog do | blog |
blog.permalink = "blog/{title}.html"
blog.sources = "posts/{year}-{month}-{day}-{title}.html"
blog.layout = "blog-layout"
end
```
### Load the articles
Time to load our articles in `index.html.erb`.
```ruby
<h1>Recent articles</h1>
<% blog.articles.each do | article | %>
<article>
<h2>
<%= article.title %>
</h2>
<%= link_to 'Read more', article %>
</article>
<% end %>
```
### Add an article layout
In the last step before we add Static CMS, we add a layout for the article page. Create a new layout `source/layouts/blog-layout.html.erb`. For now we will get the title and the content:
```ruby
<h1>
<%= current_page.data.title %>
</h1>
<%= yield %>
```
Now that we have a functioning blog, let's get started with Static CMS!
## Add Static CMS to your site
Create two files in a new folder called `admin`, `index.html` and `config.yml`. Also add an `upload` folder in the images directory that will function as our `media_folder`.
```bash
├── source
│ ├── admin
│ │ ├── index.html
│ │ ├── config.yml
│ │
│ ├── images
│ │ ├── uploads
```
In the newly created `index.html` we add scripts for Static CMS and the Netlify Identity Widget:
```html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Static CMS</title>
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js" type="text/javascript"></script>
</head>
<body>
<script src="https://unpkg.com/@staticcms/core@%5E1.0.0/dist/static-cms-core.js" type="text/javascript"></script>
</body>
</html>
```
### Configuration
For the purpose of this guide we will deploy to Netlify from a GitHub repository which requires the minimum configuration. In `config.yml` file paste the following code:
```yml
backend:
title: git-gateway
branch: main # Branch to update (optional; defaults to main)
media_folder: source/images/uploads
public_folder: /images/uploads
collections:
- name: blog
label: Blog
folder: source/posts/
extension: .html.md
format: frontmatter
create: true
slug: '{{year}}-{{month}}-{{day}}-{{title}}'
fields:
- {label: Title, name: title, widget: string}
- {label: Publish Date, name: date, widget: datetime}
- {label: Body, name: body, widget: markdown}
```
### Push to GitHub
It's now time to commit your changes and push to GitHub.
```bash
git init
git add .
git commit -m "Initial Commit"
git remote add origin https://github.com/YOUR_USERNAME/NEW_REPO_NAME.git
git push -u origin main
```
### Add your repo to Netlify
Go to Netlify and select 'New Site from Git'. Select GitHub and the repository you just pushed to. Click Configure Netlify on GitHub and give access to your repository. Finish the setup by clicking Deploy Site. Netlify will begin reading your repository and starting building your project.
### Enable Identity and Git Gateway
Netlify's Identity and Git Gateway services allow you to manage CMS admin users for your site without requiring them to have an account with your Git host or commit access on your repo. From your site dashboard on Netlify:
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you want only invited users to access your CMS, but if you're just experimenting, you can leave it open for convenience.
3. If you'd like to allow one-click login with services like Google and GitHub, check the boxes next to the services you'd like to use, under **External providers**.
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This authenticates with your Git host and generates an API access token. In this case, we're leaving the **Roles** field blank, which means any logged in user may access the CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
## Start publishing
It's time to create your first blog post. Login to your site's `/admin/` page and create a new post by clicking New Blog. Add a title, a date and some text. When you click Publish, a new commit will be created in your GitHub repo with this format `Create Blog "year-month-date-title"`.
Then Netlify will detect that there was a commit in your repo, and will start rebuilding your project. When your project is deployed you'll be able to see the post you created.
Be sure to checkout the official [Middleman Starter](https://github.com/tomrutgers/middleman-starter-netlify-cms) for more examples.

View File

@ -0,0 +1,7 @@
---
group: Migration
title: Netlify CMS Migration Guide
weight: 190
---
Guide coming soon on how to migrate your site from Netlify CMS to Static CMS!

View File

@ -0,0 +1,32 @@
---
group: Media
title: Netlify Large Media
weight: 20
---
## <img src="https://img.shields.io/badge/-Beta%20Feature-blue" alt="Beta Feature. Use at your own risk" title="Beta Feature. Use at your own risk" />
[Netlify Large Media](https://www.netlify.com/features/large-media/) is a [Git LFS](https://git-lfs.github.com/) implementation for repositories connected to Netlify sites. This means that you can use Git to work with large asset files like images, audio, and video, without bloating your repository. It does this by replacing the asset files in your repository with text pointer files, then uploading the assets to the Netlify Large Media storage service.
If you have a Netlify site with Large Media enabled, Static CMS will handle Large Media asset files seamlessly, in the same way as files stored directly in the repository.
## Requirements
To use Netlify Large Media with Static CMS, you will need to do the following:
- Configure Static CMS to use the [Git Gateway backend with Netlify Identity](/docs/git-gateway-backend).
- Configure the Netlify site and connected repository to use Large Media, following the [Large Media docs on Netlify](https://www.netlify.com/docs/large-media/).
When these are complete, you can use Static CMS as normal, and the configured asset files will automatically be handled by Netlify Large Media.
## Image transformations
All JPEG, PNG, and GIF files that are handled with Netlify Large Media also have access to Netlify's on-demand image transformation service. This service allows you to request an image to match the dimensions you specify in a query parameter added to the image URL.
You can learn more about this feature in [Netlify's image transformation docs](https://www.netlify.com/docs/image-transformation/).
### Media Gallery Thumbnails
In repositories enabled with Netlify Large Media, Static CMS will use the image transformation query parameters to load thumbnail-sized images for the media gallery view. This makes images in the media gallery load significantly faster.
**Note:** When using this option all tracked file types have to be imported into Large Media. For example if you track `*.jpg` but still have jpg-files that are not imported into Large Media the backend will throw an error. Check the [netlify docs](https://docs.netlify.com/large-media/setup/#migrate-files-from-git-history) on how to add previously committed files to Large Media.

View File

@ -0,0 +1,237 @@
---
group: Guides
title: NextJS
weight: 40
---
<Alert severity="warning">This guide is out of date and may not be currently accurate. We are working to update it. Use at your own risk.</Alert>
This guide will help you get started using Static CMS with NextJS.
## Creating a new project
Let's repeat some of the basics of setting up a simple NextJS project (check out [nextjs.org/learn](http://nextjs.org/learn) for a more detailed version).
```bash
# Create new directory and navigate into it
mkdir awesome-kitties
cd awesome-kitties
# Initialize a new project
npm init -y
# Install required dependencies
npm install --save react react-dom next
# Install webpack loader for Markdown (Use version 3+)
npm install --save-dev frontmatter-markdown-loader
# If using NextJS v11.0.0 or above, @babel/core and @babel/preset-react has to be installed as dependencies of frontmatter-markdown-loader
npm install --save-dev @babel/core @babel/preset-react
# Create folder for pages (default for NextJS), and add a index file
mkdir pages
touch pages/index.js
# Create a folder for content, and a markdown file:
mkdir content
touch content/home.md
# Create a folder for static assets
mkdir public
```
Next, we need to add some modifications to our `package.json` file to make it easier to run and deploy our new site:
```json
{
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start",
"export": "npm run build && next export"
}
}
```
There is a lot of different ways to create and display Markdown content, but to make this as easy as possible we'll be using a webpack-loader that enables us to load markdown files directly in our React components ([frontmatter-markdown-loader](https://www.npmjs.com/package/frontmatter-markdown-loader)).
Add the following content to your `content/home.md` file:
```md
---
title: Awesome kitties
date: 2019-03-17T19:31:20.591Z
cats:
- description: 'Maru is a Scottish Fold from Japan, and he loves boxes.'
title: Maru (まる)
- description: Lil Bub is an American celebrity cat known for her unique appearance.
title: Lil Bub
- description: 'Grumpy cat is an American celebrity cat known for her grumpy appearance.'
title: Grumpy cat (Tardar Sauce)
---
Welcome to my awesome page about cats of the internet.
This page is built with NextJS, and content is managed in Static CMS
```
Next, we need to tell webpack how to load Markdown files. Create a new `next.config.js` file at the root of your project with the following content:
```js
module.exports = {
webpack: (cfg) => {
cfg.module.rules.push(
{
test: /\.md$/,
loader: 'frontmatter-markdown-loader',
options: { mode: ['react-component'] }
}
)
return cfg;
}
}
```
Almost there! The last thing we need to do is to add some content to our `pages/index.js` file. With a little help of our webpack loader, we can now easily import Markdown files:
```js
import Head from "next/head"
import { Component } from 'react'
import { attributes, react as HomeContent } from '/docs/content/home.md';
export default class Home extends Component {
render() {
let { title, cats } = attributes;
return (
<>
<Head>
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
</Head>
<article>
<h1>{title}</h1>
<HomeContent />
<ul>
{cats.map((cat, k) => (
<li key={k}>
<h2>{cat.name}</h2>
<p>{cat.description}</p>
</li>
))}
</ul>
</article>
</>
)
}
}
```
Great! We now have a page that displays content from our Markdown file. Go ahead and start your development server to test if everything is working:
```bash
npm run dev
```
## Adding Static CMS
There are many different ways to add Static CMS to your project. The easiest is probably just to embed it from a CDN, and that's exactly what we're gonna do. To avoid making this guide too complicated, we're just going to add Netlify into a subfolder inside the `/public` directory (which is just served as static files by Next):
```bash
# Create and navigate into public/admin folder
mkdir -p public/admin
cd public/admin
# Create index.html and config.yml file
touch index.html
touch config.yml
```
Paste HTML for Static CMS into your `public/admin/index.html` file (check out the [Add Netlify To Your Site](/docs/add-to-your-site/) section for more information)
```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 Static CMS -->
<script src="https://unpkg.com/@staticcms/core@%5E1.0.0/dist/static-cms-core.js"></script>
</body>
</html>
```
Notice that we also added the identity widget. This allows sign up when the project is hosted at Netlify.
Paste the following configuration into your `public/admin/config.yml` file:
```yaml
backend:
name: git-gateway
branch: main # Branch to update (optional; defaults to main)
media_folder: public/img
public_folder: img
collections:
- name: "pages"
label: "Pages"
files:
- label: "Home"
name: "home"
file: "content/home.md"
fields:
- { label: "Title", name: "title", widget: "string"}
- { label: "Publish Date", name: "date", widget: "datetime" }
- { label: "Body", name: "body", widget: "markdown"}
- label: 'Cats'
name: "cats"
widget: list
fields:
- { label: "Name", name: "name", widget: "string"}
- { label: "Description", name: "description", widget: "text"}
```
Awesome! Static CMS should now be available at `localhost:3000/admin/index.html`. Unfortunately we can't edit our content just yet. First we need to move our code into a git repository, and create a new Netlify site.
**Tip:** If you want to test changes made to your config.yml file locally, swap out "git-gateway" with "test-repo" and navigate to `localhost:3000/admin/index.html` to view Static CMS locally (you can't make changes or read actual content from Git this way, but it's great to verify how things will look).
## Publishing to GitHub and Netlify
Create a new repository at GitHub (or one of the other supported git services) and follow the instructions on how to push existing files into your new repository.
Now is probably also a good time to add a `.gitignore` file:
```bash
.next/
node_modules/
/npm-debug.log
.DS_Store
out/
```
When your project is under version control, go to Netlify and select "New Site from Git". Select GitHub (or whatever service you used in the previous step), and the repository you just pushed to.
Under the final step (Build options, and deploy!), make sure you enter the following:
| Field | Value |
| ----------------- | ------------------ |
| Build command | **npm run export** |
| Publish directory | **out** |
### Enable Identity and Git Gateway
Netlify's Identity and Git Gateway services allow you to manage CMS admin users for your site without requiring them to have an account with your Git host or commit access on your repo. From your site dashboard on Netlify:
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you want only invited users to access your CMS, but if you're just experimenting, you can leave it open for convenience.
3. If you'd like to allow one-click login with services like Google and GitHub, check the boxes next to the services you'd like to use, under **External providers**.
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This authenticates with your Git host and generates an API access token. In this case, we're leaving the **Roles** field blank, which means any logged in user may access the CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
### Celebrate!
Great job - you did it! Open your new page via the new Netlify URL, and navigate to `/admin`. If you did everything correct in the previous step, you should now be able to sign up for an account, and log in.
**Tip:** Signing up with an external provider is the easiest. If you want to sign up by email, you'll have to set up a redirect in your index.js page (which we won't be covering in this guide). For more information, have a look at the [Add To Your Site](/docs/add-to-your-site) section.
Congratulations - you can finally manage your new list of cats!

272
docs/content/docs/nuxt.mdx Normal file
View File

@ -0,0 +1,272 @@
---
group: Guides
title: Nuxt
weight: 50
---
<Alert severity="warning">This guide is out of date and may not be currently accurate. We are working to update it. Use at your own risk.</Alert>
This guide will walk you through how to integrate Static CMS with Nuxt.
## Starting With `create-nuxt-app`
Follow the instructions on the [Nuxt documentation](https://nuxtjs.org/guide/installation#using-code-create-nuxt-app-code-) for creating a new project, or run:
```bash
npx create-nuxt-app <name-of-your-new-project>
cd <name-of-your-new-project>
npm run dev
```
## Setting Up Static CMS
### Add the Static CMS files to Nuxt
In the `static/` directory, create a new directory `admin/`. Inside that directory you'll create two files, your `index.html` and a `config.yml`. Per the [Static CMS documentation](/docs/add-to-your-site/), we'll set the content of `static/admin/index.html` to the following:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
<!-- Include the script that enables Netlify Identity on this page. -->
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
</head>
<body>
<!-- Include the script that builds the page and powers Static CMS -->
<script src="https://unpkg.com/@staticcms/core@%5E1.0.0/dist/static-cms-core.js"></script>
</body>
</html>
```
For your `static/admin/config.yml` file, you can put in a basic starter config:
```yaml
backend:
name: git-gateway
branch: main # Branch to update (optional; defaults to main)
media_folder: static/img
public_folder: /img
collections:
- name: 'blog'
label: 'Blog'
folder: 'content/blog'
format: 'frontmatter'
create: true
slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
editor:
preview: false
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- { label: 'Description', name: 'description', widget: 'string' }
- { label: 'Body', name: 'body', widget: 'markdown' }
```
You can build whatever collections and content modeling you want. The important thing to note is the `format: 'frontmatter'` value on each collection. This is important for consuming content in Nuxt with the [nuxt/content](https://content.nuxtjs.org) module.
### Add the `content/` directory to Nuxt
In your root directory, you can create a new directory `content/`. As you might guess, this is where our content will live. Your filesystem should look about like this, so far:
```bash
root/
├ content/
├ components/
├ layouts/
├ middleware/
├ pages/
├ plugins/
├ static/
│ └ admin/
│ ├ index.html
│ └ config.yml
├ store/
└ // .editorconfig, .gitignore, nuxt.config.js, etc...
```
### Pushing to GitHub
It's now time to commit your changes and push to GitHub. `create-nuxt-app` initializes Git automatically for you, so you only need to do:
```bash
git add .
git commit -m "Initial Commit"
git remote add origin https://github.com/YOUR_USERNAME/NEW_REPO_NAME.git
git push -u origin main
```
### Deploying With Netlify
Now you can go ahead and deploy to Netlify. Go to your Netlify dashboard and click **[New site from Git](https://app.netlify.com/start)**. Select the repo you just created. Under **Basic build settings**, you can set the build command to `npm run generate` . Set the publish directory to `dist`. Click **Deploy site** to get the process going.
### Authenticating with Netlify Identity
**Add the Netlify Identity Widget**
You've already added the Netlify Identity widget to our `admin/index.html`. The next thing to do is add the Netlify Identity widget to our site's index page. In `pages/index.vue`, we can add the following to the page `<script>` tag:
```javascript
export default {
head() {
return {
script: [{ src: 'https://identity.netlify.com/v1/netlify-identity-widget.js' }],
};
},
};
```
Once you've added this, make sure to push your changes to GitHub!
*More on adding `<script>` tags to `<head>` [here](https://nuxtjs.org/faq/#local-settings).*
**Enable Identity & Git Gateway in Netlify**
Back in your [Netlify dashboard](https://app.netlify.com/):
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Once enabled, select **Settings and usage**, and scroll down to **Registration preferences**. You can set this to either **Open** or **Invite only**, but usually **Invite only** is your best bet for a personal site.
3. If you don't want to create an account, or would like to use an external provider such as GitHub or Google, you can enable those services under **External providers**.
4. Scroll down to **Services** and click **Enable Git Gateway**.
**Accessing the CMS**
Once you've reached this point, you should be able to access the CMS in your browser at `http://localhost:3000/admin`. You'll be prompted to add the URL of your Netlify site. Once you've added that URL, you can log in with an Identity account or with one of the External Providers you enabled in step 3 above. For the sake of this tutorial, you can create a blog post in the CMS, and publish it! Once you `git pull` in your project, the blog post will show up in the project at `content/blog/<slugified-blog-post-title>.md`.
## Using nuxt/content
Static CMS and [nuxt/content](https://content.nuxtjs.org) module click together and complement each other to give you best authoring experience and developer experience respectively.
Adding nuxt/content dependency
```javascript
yarn add @nuxt/content
or
npm i @nuxt/content
```
Then, add @nuxt/content to the modules section of nuxt.config.js:
```javascript
{
modules: [
'@nuxt/content'
],
content: {
// Options
}
}
```
By adding nuxt content module you get `$content` injected into your whole app which you can use to fetch content from your content folder using `simple fetch api` or `nuxt asyncData` option.
<br />
This also gives a `<nuxt-content>` component which helps you display markdown content with ease and also gives option of live editing in dev mode.
### Example Blog Post List
`nuxt/content` module gives us `$content` which we can use to fetch the list of blog posts in `content/blog` directory.
```javascript
<template>
<div>
<li v-for="post of posts" :key="post.slug">
<NuxtLink :to="post.slug">{{ post.title }}</NuxtLink>
</li>
</div>
</template>
<script>
export default {
async asyncData({ $content }) {
const posts = await $content("blog").fetch();
return {
posts,
};
},
};
</script>
```
### Example Blog Post
To generate blog posts create a `_slug.vue` file in the pages folder. By using `$content` you would get a json which you can use to display. But if you are using `markdown` to write and store your posts you can use `<nuxt-content>` module which gives you option to edit content on page in dev mode and many more [features](https://content.nuxtjs.org/).
```javascript
<template>
<div>
<h2>{{ post.title }}</h2>
<nuxt-content :document="post" />
</div>
</template>
<script>
export default {
async asyncData({ $content, params, error }) {
let post;
try {
post = await $content("blog", params.slug).fetch();
// OR const article = await $content(`articles/${params.slug}`).fetch()
} catch (e) {
error({ message: "Blog Post not found" });
}
return {
post,
};
},
};
</script>
```
### Generating pages with the `generate` property
Since Nuxt 2.13+, nuxt export has a crawler feature integrated which will crawl all your links and generate your routes based on those links. Therefore you do not need to do anything in order for your dynamic routes to be crawled. i.e, if you are on version of nuxt above 2.14 add target as static in nuxt.config.js and use `nuxt generate` to build your static site.
```javascript
// nuxt.config.js
target: 'static'
```
If you are using **nuxt version below 2.14** you have to use generate option in nuxt/content module to generate pages
```javascript
//nuxt.config.js
export default {
modules: [,
'@nuxt/content'
],
generate: {
async routes () {
const { $content } = require('@nuxt/content')
const files = await $content().only(['path']).fetch()
return files.map(file => file.path === '/index' ? '/' : file.path)
}
}
}
```
To render your site as a static site, you'll need to create or update the `generate` property in `nuxt.config.js` to create dynamic routes and provide their content as a `payload`. In `generate`, make your `routes` entry a function:
```javascript
export default {
generate: {
routes: function() {
const fs = require('fs');
const path = require('path');
return fs.readdirSync('./content/blog').map(file => {
return {
route: `/blog/${path.parse(file).name}`, // Return the slug
payload: require(`./content/blog/${file}`),
};
});
},
},
};
```
To see the generated site, navigate to name-of-your-website.netlify.app/blog

View File

@ -0,0 +1,34 @@
---
group: Guides
title: Overview
weight: 1
---
The process for adding Static CMS to a static site can be divided into four main steps:
## Install Static CMS
This is a single page app available at the `/admin` route of your website.
Check out the [general overview](/docs/intro/) to see what the installation process entails.
## Set up a backend
This serves two purpose: Secure access to your website's Static CMS and allows it to read and update content files in your repo. More information about configuring the backend can be found [here](/docs/backends-overview/).
## Configure Static CMS using a configuration file
For starters, you can get by with a basic configuration that includes required information like Git provider, branch and folders to save files to.
Once you've gotten the hang of it, you can use the file to build whatever collections and content modeling you want. Check out the [this section](/docs/configuration-options/#configuration-file) for full details about all available configuration options.
## Render the content provided by Static CMS as web pages
Static CMS manages your content, and provides admin features, but it doesn't deliver content. It only makes your content available through an API.
It is up to developers to determine how to build the raw content into something useful and delightful on the frontend.
To learn how to query raw content managed by Static CMS and reformat them for delivery to end users, please refer the dedicated section for your site generator in the Table of Content.
## Local development
If you are experimenting with Static CMS or testing things out, you can connect it to a local Git repository instead of a live one. Learn how to do it [here](/docs/local-backend).

View File

@ -0,0 +1,246 @@
---
group: Intro
title: Start with a Template
weight: 2
---
You can add Static CMS [to an existing site](/docs/add-to-your-site/), but the quickest way to get started is with a template. Found below, our featured templates deploy a bare-bones site and Static CMS to Netlify, giving you a fully working CMS-enabled site with just a few clicks.
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: '24px' }}>
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: '8px',
}}
>
<div
style={{
width: '120px',
height: '120px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<img style={{ display: 'flex', height: '100%' }} src="/img/hugo.svg" />
</div>
<h5 style={{ margin: 0 }}>Hugo Site Starter</h5>
<p style={{ margin: 0 }}>
<a href="https://app.netlify.com/start/deploy?repository=https://github.com/StaticJsCMS/static-cms-hugo-netlify-template&amp;stack=cms">
<img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" />
</a>
</p>
</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: '8px',
}}
>
<div
style={{
width: '120px',
height: '120px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<img style={{ display: 'flex', height: '100%' }} src="/img/gatsby.svg" />
</div>
<h5 style={{ margin: 0 }}>Gatsby Site Starter</h5>
<p style={{ margin: 0 }}>
<a href="https://app.netlify.com/start/deploy?repository=https://github.com/StaticJsCMS/static-cms-gatsby-netlify-template&amp;stack=cms">
<img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" />
</a>
</p>
</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: '8px',
}}
>
<div
style={{
width: '120px',
height: '120px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<img style={{ display: 'flex', height: '100%' }} src="/img/middleman.svg" />
</div>
<h5 style={{ margin: 0 }}>Middleman Site Starter</h5>
<p style={{ margin: 0 }}>
<a href="https://app.netlify.com/start/deploy?repository=https://github.com/StaticJsCMS/static-cms-middleman-netlify-template&amp;stack=cms">
<img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" />
</a>
</p>
</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: '8px',
}}
>
<div
style={{
width: '120px',
height: '120px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<img style={{ display: 'flex', height: '100%' }} src="/img/preact.svg" />
</div>
<h5 style={{ margin: 0 }}>Preact CLI</h5>
<p style={{ margin: 0 }}>
<a href="https://app.netlify.com/start/deploy?repository=https://github.com/StaticJsCMS/static-cms-preact-netlify-template&amp;stack=cms">
<img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" />
</a>
</p>
</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: '8px',
}}
>
<div
style={{
width: '120px',
height: '120px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<img style={{ display: 'flex', height: '100%' }} src="/img/nextjs.svg" />
</div>
<h5 style={{ margin: 0 }}>Next.js Blog Template</h5>
<p style={{ margin: 0 }}>
<a href="https://app.netlify.com/start/deploy?repository=https://github.com/StaticJsCMS/static-cms-nextjs-netlify-blog-template&amp;stack=cms">
<img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" />
</a>
</p>
</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: '8px',
}}
>
<div
style={{
width: '120px',
height: '120px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<img style={{ display: 'flex', height: '100%' }} src="/img/11ty-logo.svg" />
</div>
<h5 style={{ margin: 0 }}>Eleventy Starter</h5>
<p style={{ margin: 0 }}>
<a href="https://app.netlify.com/start/deploy?repository=https://github.com/StaticJsCMS/static-cms-eleventy-netlify-template&amp;stack=cms">
<img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" />
</a>
</p>
</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: '8px',
}}
>
<div
style={{
width: '120px',
height: '120px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<img style={{ display: 'flex', height: '100%' }} src="/img/nuxt.svg" />
</div>
<h5 style={{ margin: 0 }}>Nuxt.js Boilerplate</h5>
<p style={{ margin: 0 }}>
<a href="https://app.netlify.com/start/deploy?repository=https://github.com/StaticJsCMS/static-cms-nuxt-netlify-template&amp;stack=cms">
<img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" />
</a>
</p>
</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
gap: '8px',
}}
>
<div
style={{
width: '120px',
height: '120px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<img style={{ display: 'flex', height: '100%' }} src="/img/metalsmith.svg" />
</div>
<h5 style={{ margin: 0 }}>Metalsmith Starter</h5>
<p style={{ margin: 0 }}>
<a href="https://app.netlify.com/start/deploy?repository=https://github.com/StaticJsCMS/static-cms-metalsmith-netlify-template&amp;stack=cms">
<img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" />
</a>
</p>
</div>
</div>
After clicking one of those buttons, authenticate with GitHub and choose a repository name. Netlify then automatically creates a clone of the repository in your GitHub account. Next, it builds and deploys the new site on Netlify, bringing you to the site dashboard after completing the build.
**Note for GitLab and Bitbucket users:** Static CMS supports GitLab and Bitbucket repositories, but won't work with the Deploy to Netlify buttons above without additional configuration (See [GitLab](/docs/gitlab-backend) or [Bitbucket](/docs/bitbucket-backend) respectively).
## Access Static CMS
1. The template deploy process sends you an invitation to your new site, sent from `no-reply@netlify.com`.
![Sample email subject line: You've been invited to join radiologist-amanda-53841.netlify.com](/img/email-subject.webp)
2. Wait for the deployment to complete, then click the link to accept the invite. Your site will open with a prompt to create a password.
!["Complete your signup" modal on the Kaldi coffee site](/img/create-password.webp)
3. Enter a password, sign in, and you'll go to the CMS. (For future visits, you can go straight to `<yoursiteaddress.com>/admin/`.)
Try adding and editing posts, or changing the content of the Products page. When you save, the changes are pushed immediately to your Git repository, triggering a build on Netlify, and updating the content on your site. Check out the configuration code by visiting your site repo.
## More paths to explore
- To see how to integrate Static CMS into an existing project, go to [Add to your site](/docs/add-to-your-site/).
- Check out other sites using Static CMS (or share your own!) on the [Examples](/docs/examples/) page.
- If you'd like to add more CMS editors or change how they log in to your site, read up on [Netlify Identity service](https://www.netlify.com/docs/identity).

View File

@ -0,0 +1,29 @@
---
group: Backends
title: Test Backend
weight: 60
---
- **Name**: `gitlab`
You can use the `test-repo` backend to try out Static CMS without connecting to a Git repo. With this backend, you can write and publish content normally, but any changes will disappear when you reload the page. This backend powers the Static CMS [demo site](https://static-cms-demo.netlify.app/).
**Note:** The `test-repo` backend can't access your local file system, nor does it connect to a Git repo, thus you won't see any existing files while using it.
To enable this backend, set your backend name to `test-repo` in your Static CMS `config` file.
## Example
<CodeTabs>
```yaml
backend:
name: test-repo
```
```js
backend: {
name: 'test-repo',
},
```
</CodeTabs>

View File

@ -0,0 +1,137 @@
---
group: Intro
title: Typescript
weight: 20
---
Static CMS provides first class support for Typescript when using the [Bundling option](/docs/add-to-your-site-bundling).
## Configuration
When using Typescript it is recommended to store your CMS configuration in a typescript file instead of a yaml file to take full advantage of the typings available.
```ts
import type { Config } from '@staticcms/core';
export const config: Config = {
...
}
```
### Custom Widgets
When providing your own widgets, you can extend the `Config` type with your custom widget types to provide proper typing in your config. All custom widget types should extend the `BaseField` interface.
```ts
import type { Config, BaseField } from '@staticcms/core';
export interface HtmlField extends BaseField {
widget: 'html'
default: string;
}
export interface CustomField extends BaseField {
widget: 'custom'
default: number[];
some_other_prop: string;
}
export const config: Config<HtmlField | CustomField> = {
...
}
```
## Widgets
When providing types for custom widgets you need to know the datatype your widget works with and the definition of its `Field`. The examples below assumes the field interface for the widget is in the `config.ts` file in the parent directory.
### Control Component
Control widgets take the `WidgetControlProps` interface as their props.
```tsx
import type { FC } from 'react';
import type { WidgetControlProps } from '@staticcms/core';
import type { HtmlField } from '../config';
const EditorControl: FC<WidgetControlProps<string, HtmlField>> = ({
field, // Typed as a HtmlField
value, // Typed as string | null | undefined
onChange,
openMediaLibrary,
getAsset,
mediaPaths
}) => {
...
};
```
### Preview Component
Control widgets take the `WidgetPreviewProps` interface as their props.
```tsx
import type { FC } from 'react';
import type { WidgetPreviewProps } from '@staticcms/core';
import type { HtmlField } from '../config';
const EditorPreview: FC<WidgetPreviewProps<string, HtmlField>> = ({
field, // Typed as a HtmlField
value, // Typed as string | null | undefined
getAsset,
}) => {
...
};
```
## Preview Templates
When providing types for custom preview templates you need to know the datatype of your collection (or file) that the template is represents. Preview templates take the `TemplatePreviewProps` interface as their props.
```tsx
import { useEffect, useMemo, useState } from 'react';
import type { FC } from 'react';
import type { TemplatePreviewProps } from '@staticcms/core';
interface PostPreviewData {
body: string;
date: string;
title: string;
image?: string;
slug: string;
tags?: string[];
}
const PostPreview: FC<TemplatePreviewProps<PostPreviewData>> = ({ entry, widgetFor, getAsset }) => {
const dateString = useMemo(() => entry.data.date, [entry.data.date]);
const [image, setImage] = useState('');
useEffect(() => {
let alive = true;
const loadImage = async () => {
const loadedImage = await getAsset(entry.data.image ?? '');
if (alive) {
setImage(loadedImage.toString());
}
};
loadImage();
return () => {
alive = false;
};
}, [entry.data.image, getAsset]);
return (
<div>
<h1>{entry.data.title}</h1>
<div>{entry.data.date}</div>
<img title={title} src={image} />
<div>{(entry.data.tags ?? []).join(', ')}</div>
<div>{widgetFor('body')}</div>
</div>
);
};
```

View File

@ -0,0 +1,25 @@
---
group: Migration
title: Updating Your CMS
weight: 100
---
The update procedure for your CMS depends upon the method you used to install Static CMS.
## Package Manager
If you are using a package manager like Yarn or NPM, use their standard procedure to update. This is how both the Hugo and Gatsby starters are set up.
## CDN
If you are using the CMS through a CDN like Unpkg, then that depends on the version tag you are using. You can find the version tag in the `/admin/index.html` file of your site.
- (Recommended) If you use `^1.0.0`, the CMS does all updates except major versions automatically.
- It upgrades to `1.0.1`, `1.1.0`, `1.1.1`.
- It does not upgrade to `2.0.0` or higher.
- It does not upgrade to beta versions.
- If you use `~1.0.0`, the CMS will do only patch updates automatically.
- It upgrades `1.0.1`, `1.0.2`.
- It does not upgrade to `1.1.0` or higher.
- It does not upgrade beta versions.

View File

@ -0,0 +1,122 @@
---
group: Media
title: Uploadcare
weight: 30
---
## <img src="https://img.shields.io/badge/-Beta%20Feature-blue" alt="Beta Feature. Use at your own risk" title="Beta Feature. Use at your own risk" />
Uploadcare is a sleek service that allows you to upload files without worrying about maintaining a growing collection — more of an asset store than a library. Just upload when you need to, and the files are hosted on their CDN. They provide image processing controls from simple cropping and rotation to filters and face detection, and a lot more. You can check out Uploadcare's full feature set on their [website](https://uploadcare.com/).
The Uploadcare media library integration for Static CMS allows you to use Uploadcare as your media handler within the CMS itself. It's available by default as of our 2.1.0 release, and works in tandem with the existing file and image widgets, so using it only requires creating an Uploadcare account and updating your Static CMS configuration.
**Please make sure that Static CMS is updated to 2.1.0 or greater before proceeding.**
## Creating an Uploadcare Account
You can [sign up](https://uploadcare.com/accounts/signup/) for a free Uploadcare account to get started. Once you've signed up, go to your dashboard, select a project, and then select "API keys" from the menu on the left. The public key on the API keys page will be needed in your Static CMS configuration. For more info on getting your API key, visit their [walkthrough](https://uploadcare.com/docs/keys/).
## Updating Static CMS Configuration
The next and final step is updating your Static CMS configuration file:
1. Add a `media_library` property at the same level as `media_folder`, with an object as it's value.
2. In the `media_library` object, add the name of the media player under `name`.
3. Add a `config` object under name with a `publicKey` property with your Uploadcare public key as it's value.
Your `config` should now include something like this (except with a real API key):
<CodeTabs>
```yaml
media_library:
name: uploadcare
config:
publicKey: YOUR_UPLOADCARE_PUBLIC_KEY
```
```js
media_library: {
name: 'uploadcare',
config: {
publicKey: 'YOUR_UPLOADCARE_PUBLIC_KEY',
},
},
```
</CodeTabs>
Once you've finished updating your Static CMS configuration, the Uploadcare widget will appear when using the image or file widgets.
**Note:** You'll need to [register the media libraries yourself](/blog/2019/07/netlify-cms-gatsby-plugin-4-0-0#using-media-libraries-with-netlify-cms-app).
## Configuring the Uploadcare Widget
The Uploadcare widget can be configured with settings that are outlined [in their docs](https://uploadcare.com/docs/file_uploads/widget/options/). The widget itself accepts configuration through global variables and data properties on HTML elements, but with Static CMS you can pass configuration options directly through your `config`.
**Note:** all default values described in Uploadcare's documentation also apply in the Static CMS integration, except for `previewStep`, which is set to `true`. This was done because the preview step provides helpful information like upload status, and provides access to image editing controls. This option can be disabled through the configuration options below.
### Global configuration
Global configuration, which is meant to affect the Uploadcare widget at all times, can be provided as seen above, under the primary `media_library` property. Settings applied here will affect every instance of the Uploadcare widget.
## Field configuration
Configuration can also be provided for individual fields that use the media library. The structure is very similar to the global configuration, except the settings are added to an individual `field`. For example:
<CodeTabs>
```yaml
fields:
name: cover
label: Cover Image
widget: image
media_library:
config:
multiple: true
previewStep: false
```
```js
fields: {
name: 'cover',
label: 'Cover Image',
widget: 'image',
media_library: {
config: {
multiple: true,
previewStep: false,
},
},
},
```
</CodeTabs>
## Integration settings
There are several settings that control the behavior of integration with the widget.
* `autoFilename` (`boolean`) - specify whether to add a filename to the end of the url. Example: `http://ucarecdn.com/:uuid/filename.png`
* `defaultOperations` (`string`) - specify a string added at the end of the url. This could be useful to apply a set of CDN operations to each image, for example resizing or compression. All the possible operations are listed [here](https://uploadcare.com/docs/api_reference/cdn/).
<CodeTabs>
```yaml
media_library:
name: uploadcare
config:
publicKey: YOUR_UPLOADCARE_PUBLIC_KEY
settings:
autoFiletitle: true
defaultOperations: '/resize/800x600/'
```
```js
media_library: {
name: 'uploadcare',
config: {
publicKey: 'YOUR_UPLOADCARE_PUBLIC_KEY',
},
settings: {
autoFiletitle: true,
defaultOperations: '/resize/800x600/',
},
},
```
</CodeTabs>

View File

@ -0,0 +1,38 @@
---
group: Widgets
title: Boolean
weight: 10
---
- **Name**: `boolean`
- **UI**: Toggle switch
- **Data type**: `boolean`
The boolean widget translates a toggle switch input to a `true` or `false` value.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ------- | ------- | ------- | ------------------------------------------- |
| default | boolean | `false` | _Optional_. The default value for the field |
## Example
<CodeTabs>
```yaml
name: draft
label: Draft
widget: boolean
default: true
```
```js
name: 'draft',
label: 'Draft',
widget: 'boolean',
default: true,
```
</CodeTabs>

View File

@ -0,0 +1,40 @@
---
group: Widgets
title: Code
weight: 11
---
- **Name**: `code`
- **UI**: Codemirror editor
- **Data type**: `string` or `{ code: 'My code here', lang: 'javascript' }`
The code widget provides a code editor (powered by [Codemirror](https://codemirror.net)) with optional syntax awareness. Can output the raw code value or an object with the selected language and the raw code value.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ------------------------ | ------- | -------------------------------- | ------------------------------------------------------------------------------------ |
| default_language | string | | _Optional_. Default language to use |
| allow_language_selection | boolean | `false` | _Optional_. Allows language syntax to be changed |
| keys | boolean | `{ code: 'code', lang: 'lang' }` | _Optional_. Sets key names for code and lang if outputting an object |
| output_code_only | string | `true` | _Optional_. Set to `true` to output the string value only |
| code_mirror_config | boolean | `false` | _Optional_. Config options for [codemiror](https://codemirror.net/5/doc/manual.html) |
## Example
<CodeTabs>
```yaml
name: code
label: Code
widget: code
```
```js
name: 'code',
label: 'Code',
widget: 'code',
```
</CodeTabs>

View File

@ -0,0 +1,61 @@
---
group: Widgets
title: Color
weight: 12
---
- **Name**: `color`
- **UI**: Color picker
- **Data type**: `string`
The color widget translates a color picker to a color string.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ------------ | ------- | ------- | ---------------------------------------------------------- |
| default | string | `''` | _Optional_. The default value for the field |
| allow_input | boolean | `false` | _Optional_. Allows manual editing of the color input value |
| enable_alpha | boolean | `false` | _Optional_. Enables Alpha editing |
## Examples
### Basic
<CodeTabs>
```yaml
name: color
label: Color
widget: color
```
```js
name: 'color',
label: 'Color',
widget: 'color',
```
</CodeTabs>
### Kitchen Sink
<CodeTabs>
```yaml
name: color
label: Color
widget: color
enable_alpha: true
allow_input: true
```
```js
name: 'color',
label: 'Color',
widget: 'color',
enable_alpha: true,
allow_input: true,
```
</CodeTabs>

View File

@ -0,0 +1,94 @@
---
group: Widgets
title: DateTime
weight: 13
---
- **Name**: `datetime`
- **UI**: Datetime picker
- **Data type**: [date-fns](https://date-fns.org/) formatted datetime string
The datetime widget translates a datetime picker to a datetime string.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ----------- | ---------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| default | string | `Current Date and Time` | _Optional_. The default value for the field. Accepts a datetime string, or an empty string to accept blank input. |
| format | string | `yyyy-MM-dd'T'HH:mm:ss.SSSXXX` | _Optional_. Sets storage format. Accepts [date-fns tokens](https://date-fns.org/v2.29.3/docs/format) |
| date_format | string<br />\| boolean | `true` | _Optional_. Sets date display format in UI.<ul><li>`string` - Accepts [date-fns tokens](https://date-fns.org/v2.29.3/docs/format)</li><li>`true` - Uses default locale format</li><li>`false` - If `time_format` is `true` or a string, then date picker is hidden</li></ul> |
| time_format | string<br />\| boolean | `true` | _Optional_. Sets time display format in UI.<ul><li>`string` - Accepts [date-fns tokens](https://date-fns.org/v2.29.3/docs/format)</li><li>`true` - Uses default locale format</li><li>`false` - Hides the time picker</li></ul> |
| picker_utc | boolean | `false` | _Optional_. <ul><li>`true` - The datetime picker will display times in UTC</li><li>`false` - The datetime picker will display times in the user's local timezone</li></ul> When using date-only formats, it can be helpful to set this to `true` so users in all timezones will see the same date in the datetime picker |
## Examples
### Date Time Picker
<CodeTabs>
```yaml
name: 'datetime'
label: 'Datetime'
widget: 'datetime'
date_format: 'dd.MM.yyyy' # e.g. 24.12.2022
time_format: 'HH:mm' # e.g. 21:07
format: 'yyyy-MM-dd HH:mm' # e.g. 2022-12-24 21:07
```
```js
name: 'datetime',
label: 'Datetime',
widget: 'datetime',
date_format: 'dd.MM.yyyy', // e.g. 24.12.2022
time_format: 'HH:mm', // e.g. 21:07
format: 'yyyy-MM-dd HH:mm', // e.g. 2022-12-24 21:07
```
</CodeTabs>
### Date Picker
<CodeTabs>
```yaml
name: 'date'
label: 'Date'
widget: 'datetime'
date_format: 'dd.MM.yyyy' # e.g. 24.12.2022
time_format: false
format: 'yyyy-MM-dd' # e.g. 2022-12-24
```
```js
name: 'date',
label: 'Date',
widget: 'datetime',
date_format: 'dd.MM.yyyy', // e.g. 24.12.2022
time_format: false,
format: 'yyyy-MM-dd', // e.g. 2022-12-24
```
</CodeTabs>
### Time Picker
<CodeTabs>
```yaml
name: 'date'
label: 'Date'
widget: 'datetime'
date_format: false
time_format: 'HH:mm' # e.g. 21:07
format: 'HH:mm' # e.g. 21:07
```
```js
name: 'date',
label: 'Date',
widget: 'datetime',
date_format: false,
time_format: 'HH:mm', // e.g. 21:07
format: 'HH:mm', // e.g. 21:07
```
</CodeTabs>

View File

@ -0,0 +1,59 @@
---
group: Widgets
title: File
weight: 14
---
- **Name:** `file`
- **UI:** File picker button opens media gallery
- **Data type:** File path string
The file widget allows editors to upload a file or select an existing one from the media library. The path to the file will be saved to the field as a string.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ------------- | --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| default | string | `null` | _Optional_. The default value for the field. Accepts a datetime string, or an empty string to accept blank input; otherwise defaults to current datetime |
| media_library | Media Library Options | `{}` | _Optional_. Media library settings to apply when a media library is opened by the current widget. See [Media Library Options](#media-library-options) |
| media_folder | string | | _Optional_. Specifies the folder path where uploaded files should be saved, relative to the base of the repo |
| public_folder | string | | _Optional_. Specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site |
### Media Library Options
| Name | Type | Default | Description |
| -------------- | ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| allow_multiple | boolean | `true` | _Optional_. When set to `false`, prevents multiple selection for any media library extension, but must be supported by the extension in use |
| config | string | `{}` | _Optional_. A configuration object that will be passed directly to the media library being used - available options are determined by the library |
| choose_url | string<br />\| boolean | `true` | _Optional_. When set to `false`, the "Insert from URL" button will be hidden |
## Example
<CodeTabs>
```yaml
name: manual_pdf
label: Manual PDF
widget: file
default: /uploads/general-manual.pdf
media_library:
choose_url: true
config:
multiple: true
```
```js
name: 'manual_pdf',
label: 'Manual PDF',
widget: 'file',
default: '/uploads/general-manual.pdf',
media_library: {
choose_url: true,
config: {
multiple: true,
},
},
```
</CodeTabs>

View File

@ -0,0 +1,38 @@
---
group: Widgets
title: Hidden
weight: 15
---
- **Name:** `hidden`
- **UI:** None
- **Data type:** Any valid data type
Hidden widgets do not display in the UI. In folder collections that allow users to create new items, you will often want to set a default for hidden fields, so they will be set without requiring an input.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ------- | ------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| default | string | `null` | _Optional_. The default value for the field. Accepts any valid data type. Recommended for collections that allow adding new items. |
## Example
<CodeTabs>
```yaml
name: layout
label: Layout
widget: hidden
default: blog
```
```js
name: 'layout',
label: 'Layout',
widget: 'hidden',
default: 'blog',
```
</CodeTabs>

View File

@ -0,0 +1,59 @@
---
group: Widgets
title: Image
weight: 16
---
- **Name:** `image`
- **UI:** File picker button opens media gallery allowing image files (jpg, jpeg, webp, gif, png, bmp, tiff, svg) only; displays selected image thumbnail
- **Data type:** File path string
The file widget allows editors to upload a file or select an existing one from the media library. The path to the file will be saved to the field as a string.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ------------- | --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| default | string | `null` | _Optional_. The default value for the field. Accepts a datetime string, or an empty string to accept blank input; otherwise defaults to current datetime |
| media_library | Media Library Options | `{}` | _Optional_. Media library settings to apply when a media library is opened by the current widget. See [Media Library Options](#media-library-options) |
| media_folder | string | | _Optional_. Specifies the folder path where uploaded files should be saved, relative to the base of the repo |
| public_folder | string | | _Optional_. Specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site |
### Media Library Options
| Name | Type | Default | Description |
| -------------- | ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| allow_multiple | boolean | `true` | _Optional_. When set to `false`, prevents multiple selection for any media library extension, but must be supported by the extension in use |
| config | string | `{}` | _Optional_. A configuration object that will be passed directly to the media library being used - available options are determined by the library |
| choose_url | string<br />\| boolean | `true` | _Optional_. When set to `false`, the "Insert from URL" button will be hidden |
## Example
<CodeTabs>
```yaml
name: thumbnail
label: Featured Image
widget: image
default: /uploads/chocolate-dogecoin.jpg
media_library:
choose_url: true
config:
multiple: true
```
```js
name: 'thumbnail',
label: 'Featured Image',
widget: 'image',
default: '/uploads/chocolate-dogecoin.jpg',
media_library: {
choose_url: true,
config: {
multiple: true,
},
},
```
</CodeTabs>

View File

@ -0,0 +1,520 @@
---
group: Widgets
title: List
weight: 17
---
- **Name:** `list`
- **UI:** The list widget contains a repeatable child widget, with controls for adding, deleting, and re-ordering the repeated widgets.
- **Data type:** List of widget values
The list widget allows you to create a repeatable item in the UI which saves as a list of widget values. You can choose any widget as a child of a list widget—even other lists.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| -------------- | ---------------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| default | string | `Single item`<br />`(with child defaults)` | _Optional_. The default value for the field. Accepts an array of list items |
| allow_add | boolean | `true` | _Optional_. `false` - Hides the button to add additional items |
| collapsed | boolean | `true` | _Optional_. `true` - The entries collapse by default |
| summary | string | | _Optional_. The label displayed on collapsed entries |
| label_singular | string | `label` | _Optional_. The text to show on the add button |
| fields | list of widgets | [] | _Optional_. A nested list of multiple widget fields to be included in each repeatable iteration |
| min | number | | _Optional_. Minimum number of items in the list |
| max | number | | _Optional_. Maximum number of items in the list |
| add_to_top | boolean | `false` | _Optional_. <ul><li>`true` - New entries will be added to the top of the list</li><li>`false` - New entries will be added to the bottom of the list</li></ul> |
| types | list of object widgets | | _Optional_. A nested list of object widgets representing the available types for items in the list. Takes priority over `fields`. |
| type_key | string | `'type'` | _Optional_. The name of the field that will be added to every item in list representing the name of the object widget that item belongs to. Ignored if `types` is not defined |
## Examples
### Basic
<CodeTabs>
```yaml
name: testimonials
label: Testimonials
widget: list
summary: '{{fields.quote}} - {{fields.author.name}}'
fields:
- name: quote
label: Quote
widget: string
default: Everything is awesome!
- name: author
label: Author
widget: object
fields:
- name: name
label: Name
widget: string
default: Emmet
- name: avatar
label: Avatar
widget: image
default: /img/emmet.jpg
```
```js
name: 'testimonials',
label: 'Testimonials',
widget: 'list',
summary: '{{fields.quote}} - {{fields.author.name}}',
fields: [
{
name: 'quote',
label: 'Quote',
widget: 'string',
default: 'Everything is awesome!'
},
{
name: 'author',
label: 'Author',
widget: 'object',
fields: [
{
name: 'name',
label: 'Name',
widget: 'string',
default: 'Emmet'
},
{
name: 'avatar',
label: 'Avatar',
widget: 'image',
default: '/img/emmet.jpg'
},
],
},
],
```
</CodeTabs>
### Allow Additions
<CodeTabs>
```yaml
name: testimonials
label: Testimonials
widget: list
summary: '{{fields.quote}} - {{fields.author.name}}'
allow_add: false
fields:
- name: quote
label: Quote
widget: string
default: Everything is awesome!
- name: author
label: Author
widget: object
fields:
- name: name
label: Name
widget: string
default: Emmet
- name: avatar
label: Avatar
widget: image
default: /img/emmet.jpg
```
```js
name: 'testimonials',
label: 'Testimonials',
widget: 'list',
summary: '{{fields.quote}} - {{fields.author.name}}',
allow_add: false,
fields: [
{
name: 'quote',
label: 'Quote',
widget: 'string',
default: 'Everything is awesome!'
},
{
name: 'author',
label: 'Author',
widget: 'object',
fields: [
{
name: 'name',
label: 'Name',
widget: 'string',
default: 'Emmet'
},
{
name: 'avatar',
label: 'Avatar',
widget: 'image',
default: '/img/emmet.jpg'
},
],
},
],
```
</CodeTabs>
### Default Value
<CodeTabs>
```yaml
name: galleryImages
label: Gallery
widget: list
fields:
- name: src
label: Source
widget: string
- name: alt
label: Alt Text
widget: string
default:
- src: /img/tennis.jpg
alt: Tennis
- src: /img/footbar.jpg
alt: Football
```
```js
name: 'galleryImages',
label: 'Gallery',
widget: 'list',
fields: [
{
name: 'src',
label: 'Source',
widget: 'string'
},
{
name: 'alt',
label: 'Alt Text',
widget: 'string'
},
],
default: [
{
src: '/img/tennis.jpg',
alt: 'Tennis'
},
{
src: '/img/footbar.jpg',
alt: 'Football'
},
],
```
</CodeTabs>
### Start Collapsed
<CodeTabs>
```yaml
name: testimonials
label: Testimonials
widget: list
summary: '{{fields.quote}} - {{fields.author.name}}'
collapsed: false
fields:
- name: quote
label: Quote
widget: string
default: Everything is awesome!
- name: author
label: Author
widget: object
fields:
- name: name
label: Name
widget: string
default: Emmet
- name: avatar
label: Avatar
widget: image
default: /img/emmet.jpg
```
```js
name: 'testimonials',
label: 'Testimonials',
widget: 'list',
summary: '{{fields.quote}} - {{fields.author.name}}',
collapsed: false,
fields: [
{
name: 'quote',
label: 'Quote',
widget: 'string',
default: 'Everything is awesome!'
},
{
name: 'author',
label: 'Author',
widget: 'object',
fields: [
{
name: 'name',
label: 'Name',
widget: 'string',
default: 'Emmet'
},
{
name: 'avatar',
label: 'Avatar',
widget: 'image',
default: '/img/emmet.jpg'
},
],
},
],
```
</CodeTabs>
### Min and Max
<CodeTabs>
```yaml
name: testimonials
label: Testimonials
widget: list
summary: '{{fields.quote}} - {{fields.author.name}}'
min: 1
max: 3
fields:
- name: quote
label: Quote
widget: string
default: Everything is awesome!
- name: author
label: Author
widget: object
fields:
- name: name
label: Name
widget: string
default: Emmet
- name: avatar
label: Avatar
widget: image
default: /img/emmet.jpg
```
```js
name: 'testimonials',
label: 'Testimonials',
widget: 'list',
summary: '{{fields.quote}} - {{fields.author.name}}',
min: 1,
max: 3,
fields: [
{
name: 'quote',
label: 'Quote',
widget: 'string',
default: 'Everything is awesome!'
},
{
name: 'author',
label: 'Author',
widget: 'object',
fields: [
{
name: 'name',
label: 'Name',
widget: 'string',
default: 'Emmet'
},
{
name: 'avatar',
label: 'Avatar',
widget: 'image',
default: '/img/emmet.jpg'
},
],
},
],
```
</CodeTabs>
### Add To Top
<CodeTabs>
```yaml
name: testimonials
label: Testimonials
widget: list
summary: '{{fields.quote}} - {{fields.author.name}}'
add_to_top: true
fields:
- name: quote
label: Quote
widget: string
default: Everything is awesome!
- name: author
label: Author
widget: object
fields:
- name: name
label: Name
widget: string
default: Emmet
- name: avatar
label: Avatar
widget: image
default: /img/emmet.jpg
```
```js
name: 'testimonials',
label: 'Testimonials',
widget: 'list',
summary: '{{fields.quote}} - {{fields.author.name}}',
add_to_top: true,
fields: [
{
name: 'quote',
label: 'Quote',
widget: 'string',
default: 'Everything is awesome!'
},
{
name: 'author',
label: 'Author',
widget: 'object',
fields: [
{
name: 'name',
label: 'Name',
widget: 'string',
default: 'Emmet'
},
{
name: 'avatar',
label: 'Avatar',
widget: 'image',
default: '/img/emmet.jpg'
},
],
},
],
```
</CodeTabs>
### Typed List
<CodeTabs>
```yaml
name: sections
label: Home Section
widget: list
types:
- name: carousel
label: Carousel
widget: object
summary: '{{fields.header}}'
fields:
- name: header
label: Header
widget: string
default: Image Gallery
- name: template
label: Template
widget: string
default: carousel.html
- name: images
label: Images
widget: list
fields:
- name: image
label: Image
widget: image
- name: spotlight
label: Spotlight
widget: object
fields:
- name: header
label: Header
widget: string
default: Spotlight
- name: template
label: Template
widget: string
default: spotlight.html
- name: text
label: Text
widget: text
default: Hello World
```
```js
name: 'sections',
label: 'Home Section',
widget: 'list',
types: [
{
name: 'carousel',
label: 'Carousel',
widget: 'object',
summary: '{{fields.header}}',
fields: [
{
name: 'header',
label: 'Header',
widget: 'string',
default: 'Image Gallery'
},
{
name: 'template',
label: 'Template',
widget: 'string',
default: 'carousel.html'
},
{
name: 'images',
label: 'Images',
widget: 'list',
fields: [
{
name: 'image',
label: 'Image',
widget: 'image'
}
],
},
],
},
{
name: 'spotlight',
label: 'Spotlight',
widget: 'object',
fields: [
{
name: 'header',
label: 'Header',
widget: 'string',
default: 'Spotlight'
},
{
name: 'template',
label: 'Template',
widget: 'string',
default: 'spotlight.html'
},
{
name: 'text',
label: 'Text',
widget: 'text',
default: 'Hello World'
},
],
},
],
```
</CodeTabs>

View File

@ -0,0 +1,39 @@
---
group: Widgets
title: Map
weight: 18
---
- **Name:** `map`
- **UI:** Interactive map
- **Data type:** `GeoJSON string``
The map widget allows you to edit spatial data using an interactive map. Spatial data for a single piece of geometry saves as a GeoJSON string in WGS84 projection.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| -------- | ---------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------- |
| default | string | `''` | _Optional_. The default value for the field. Accepts a GeoJSON string containing a single geometry |
| decimals | number | `7` | _Optional_. Precision of saved coordinates |
| type | 'Point'<br />\| 'LineString'<br />\| 'Polygon' | `'Point'` | _Optional_. Data type |
| height | string | `'400px'` | _Optional_. Height of map element |
## Example
<CodeTabs>
```yaml
name: location
label: Location
widget: map
```
```js
name: 'location',
label: 'Location',
widget: 'map',
```
</CodeTabs>

View File

@ -0,0 +1,115 @@
---
group: Widgets
title: Markdown
weight: 19
---
- **Name:** `markdown`
- **UI:** [Toast UI Editor](https://ui.toast.com/tui-editor) ([Docs](https://nhn.github.io/tui.editor/latest/))
- **Data type:** `markdown string`
The markdown widget provides a full fledged text editor allowing users to format text with features such as headings and blockquotes. Users can change their editing view with a handy toggle button.
_Please note:_ If you want to use your markdown editor to fill a markdown file contents after its frontmatter, you'll have to name the field `body` so the CMS recognizes it and saves the file accordingly.
## Widget Options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ------------- | --------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| default | string | `''` | _Optional_. The default value for the field. Accepts markdown content |
| media_library | Media Library Options | `{}` | _Optional_. Media library settings to apply when a media library is opened by the current widget. See [Media Library Options](#media-library-options) |
| media_folder | string | | _Optional_. Specifies the folder path where uploaded files should be saved, relative to the base of the repo |
| public_folder | string | | _Optional_. Specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site |
### Media Library Options
| Name | Type | Default | Description |
| -------------- | ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| allow_multiple | boolean | `true` | _Optional_. When set to `false`, prevents multiple selection for any media library extension, but must be supported by the extension in use |
| config | string | `{}` | _Optional_. A configuration object that will be passed directly to the media library being used - available options are determined by the library |
| choose_url | string<br />\| boolean | `true` | _Optional_. When set to `false`, the "Insert from URL" button will be hidden |
## Example
<CodeTabs>
```yaml
name: body
label: Blog post content
widget: markdown
```
```js
name: 'body',
label: 'Blog post content',
widget: 'markdown',
```
</CodeTabs>
This would render as:
![Markdown widget example](/img/widgets-markdown.webp)
_Please note:_ The markdown widget outputs a raw markdown string. Your static site generator may or may not render the markdown to HTML automatically. Consult with your static site generator's documentation for more information about rendering markdown.
## Customization
Several customization options are available for the markdown editor. You can register the options by calling `setMarkdownEditorOptions` (also available on the global `window.CMS`).
### Available Options
| Name | Type | Default | Description |
| --------------- | ---------------------------- | ----------------------------------- | --------------------------------------------------------------------------- |
| initialEditType | 'markdown'<br />\| 'wysiwyg' | `'wysiwyg'` | _Optional_. Sets which editor view that is active when the editor is loaded |
| height | string | `'600px'` | _Optional_. Specify the height of the editor |
| toolbarItems | factory of list of strings | See [Toolbar Items](#toolbar-items) | _Optional_. See [Toolbar Items](#toolbar-items) |
| plugins | list of plugin factories | | _Optional_. See [Plugins](#plugins) |
### Toolbar Items
`toolbarItems` accepts a factory function that returns a list of toolbar buttons for the editor. See the [ToastUI Editor toolbar docs](https://github.com/nhn/tui.editor/blob/master/docs/en/toolbar.md).
#### Default Value
```js
[
['heading', 'bold', 'italic', 'strike'],
['hr', 'quote'],
['ul', 'ol', 'task', 'indent', 'outdent'],
['table', imageToolbarButton, 'link'],
['code', 'codeblock'],
];
```
#### Factory Props
| Name | Type | Description |
| ------------------ | ------------------ | ----------------------------------------------------------- |
| imageToolbarButton | ToolbarItemOptions | An image insert button tied into Static CMS's media library |
### Plugins
`plugins` accepts a list of factory functions that returns a plugin for the editor. See the [ToastUI Editor plugins docs](https://github.com/nhn/tui.editor/blob/master/docs/en/plugin.md).
#### Default Value
```js
[imagePlugin];
```
#### Factory Props
| Name | Type | Description |
| ------ | ----------------------- | ---------------------------------------------------------------------------------------------- |
| config | Config | The current Static CMS config. See [configuration options](/docs/configuration-options) |
| field | MarkdownField | The field configuration for the current Markdown widget. See [Widget Options](#widget-options) |
| media | MediaHolder | See [Media Holder](#media-holder) |
| mode | 'editor'<br />\| 'preview' | Specifies if your plugin is running in the markdown editor or the markdown preview |
##### Media Holder
Media holder is a javascript class that holds the loaded media assets (images or files) that are present in the markdown content. It exposes a method called `getMedia` that takes a `url` and returns the loaded image or file as an blob asset.
This is utilized by the `imagePlugin` to be able to render images present in the markdown that are currently only available in backend or are not yet persisted to the backend.

View File

@ -0,0 +1,50 @@
---
group: Widgets
title: Number
weight: 20
---
- **Name:** `number`
- **UI:** HTML [number input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number)
- **Data type:** `string` or `number`. Configured by `value_type` option
The number widget uses an HTML number input, saving the value as a string, integer, or floating point number.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ---------- | ------------------------------------ | ---------- | ----------------------------------------------------------------------------------- |
| default | string<br />\| number | `''` | _Optional_. The default value for the field. Accepts a string or number |
| value_type | 'int'<br />\| 'float'<br />\| string | `'string'` | _Optional_. Accepts `int` or `float`; any other value results in saving as a string |
| min | number | | _Optional_. Minimum value accepted |
| max | number | | _Optional_. Maximum value accepted |
| step | number | `1` | _Optional_. Size of steps when stepping up or down in input |
## Example
<CodeTabs>
```yaml
name: 'puppies'
label: 'Puppy Count'
widget: 'number'
default: 2
value_type: 'int'
min: 1
max: 101
step: 2
```
```js
name: 'puppies',
label: 'Puppy Count',
widget: 'number',
default: 2,
value_type: 'int',
min: 1,
max: 101,
step: 2,
```
</CodeTabs>

View File

@ -0,0 +1,100 @@
---
group: Widgets
title: Object
weight: 21
---
- **Name:** `object`
- **UI:** a field containing one or more child widgets
- **Data type:** Object of child widget values
The object widget allows you to group multiple widgets together, nested under a single field. You can choose any widget as a child of an object widget, even other object widgets.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| --------- | ------- | ------- | ------------------------------------------------------------ |
| fields | boolean | `false` | A nested list of widget fields to include in your widget |
| collapsed | boolean | `false` | _Optional_. Collapse the widget's content by default |
| summary | string | `value` | _Optional_. The label displayed when the object is collapsed |
_Please note:_ A default value cannot be set directly on an object widget. Instead you can set defaults within each sub-field's configuration
## Example
<CodeTabs>
```yaml
name: 'profile'
label: 'Profile'
widget: 'object'
summary: '{{fields.name}}: {{fields.birthdate}}'
fields:
- name: public
label: Public
widget: boolean
default: true
- name: name
label: Name
widget: string
- name: 'birthdate'
label: 'Birthdate'
widget: 'date'
default: ''
format: 'MM/DD/YYYY'
- name: 'address'
label: 'Address'
widget: 'object'
collapsed: true
fields:
- name: street
label: Street Address
widget: string
- name: city
label: City
widget: string
- name: post-code
label: Postal Code
widget: string
```
```js
name: 'profile',
label: 'Profile',
widget: 'object',
summary: '{{fields.name}}: {{fields.birthdate}}',
fields: [
{
name: 'public',
label: 'Public',
widget: 'boolean',
default: true
},
{
name: 'name',
label: 'Name',
widget: 'string'
},
{
name: 'birthdate',
label: 'Birthdate',
widget: 'date',
default: '',
format: 'MM/DD/YYYY'
},
{
name: 'address',
label: 'Address',
widget: 'object',
collapsed: true,
fields: [
{ name: 'street', label: 'Street Address', widget: 'string' },
{ name: 'city', label: 'City', widget: 'string' },
{ name: 'post-code', label: 'Postal Code', widget: 'string' },
],
},
],
```
</CodeTabs>

View File

@ -0,0 +1,119 @@
---
group: Widgets
title: Relation
weight: 22
---
- **Name:** `relation`
- **UI:** Text input with search result dropdown
- **Data type:** Data type of the value pulled from the related collection item
The relation widget allows you to reference items from another collection. It provides a search input with a list of entries from the collection you're referencing, and the list automatically updates with matched entries based on what you've typed.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| -------------- | -------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| collection | string | | Name of the referenced collection |
| value_field | string | | Name of the field from the referenced collection whose value will be stored for the relation. For nested fields, separate each subfield with a `.` (e.g. `name.first`). For list fields use a wildcard `*` to target all list items (e.g. `categories.*`) |
| search_fields | list of strings | | List of one or more names of fields in the referenced collection to search for the typed value. Syntax to reference nested fields is similar to that of _value_field_ |
| default | Any widget data type | `''` | _Optional_. The default value for the field. Accepts any widget data type |
| file | string | | _Optional_. Allows referencing a specific file when the referenced collection is a files collection |
| display_fields | list of strings | | _Optional_. list of one or more names of fields in the referenced collection that will render in the autocomplete menu of the control. Defaults to `value_field`. Syntax to reference nested fields is similar to that of _value_field_ |
| multiple | boolean | `false` | _Optional_. Allow multiple values to be selected |
| min | number | | _Optional_. Minimum number of items. Ignored if **multiple** is `false` |
| max | number | | _Optional_. Maximum number of items. Ignored if **multiple** is `false` |
| options_length | number | `20` | _Optional_. Number of options presented to user |
## Examples
### Referencing A Folder Collection
_Assuming a separate "authors" collection with "name" and "twitterHandle" fields with subfields "first" and "last" for the "name" field._
<CodeTabs>
```yaml
name: author
label: Post Author
widget: relation
collection: authors
search_fields: ['name.first', 'twitterHandle']
value_field: name.first
display_fields: ['twitterHandle', 'followerCount']
```
```js
name: 'author',
label: 'Post Author',
widget: 'relation',
collection: 'authors',
search_fields: ['name.first', 'twitterHandle'],
value_field: 'name.first',
display_fields: ['twitterHandle', 'followerCount'],
```
</CodeTabs>
The generated UI input will search the authors collection by name and twitterHandle, and display each author's handle and follower count. On selection, the author's name is saved for the field.
### String Template
_Assuming a separate "authors" collection with "name" and "twitterHandle" fields with subfields "first" and "last" for the "name" field._
<CodeTabs>
```yaml
name: author
label: Post Author
widget: relation
collection: authors
search_fields: ['name.first']
value_field: '{{slug}}'
display_fields: ['{{twitterHandle}} - {{followerCount}}']
```
```js
name: 'author',
label: 'Post Author',
widget: 'relation',
collection: 'authors',
search_fields: ['name.first'],
value_field: '{{slug}}',
display_fields: ['{{twitterHandle}} - {{followerCount}}'],
```
</CodeTabs>
The generated UI input will search the authors collection by name, and display each author's handle and follower count. On selection, the author entry slug is saved for the field.
### Referencing A File Collection List Field
_Assuming a separate "relation_files" collection with a file named "cities" with a list field "cities" with subfields "name" and "id"._
<CodeTabs>
```yaml
name: city
label: City
widget: relation
collection: relation_files
file: cities
search_fields: ['cities.*.name']
display_fields: ['cities.*.name']
value_field: 'cities.*.id'
```
```js
name: 'city',
label: 'City',
widget: 'relation',
collection: 'relation_files',
file: 'cities',
search_fields: ['cities.*.name'],
display_fields: ['cities.*.name'],
value_field: 'cities.*.id',
```
</CodeTabs>
The generated UI input will search the cities file by city name, and display each city's name. On selection, the city id is saved for the field.

View File

@ -0,0 +1,169 @@
---
group: Widgets
title: Select
weight: 23
---
- **Name:** `select`
- **UI:** Select input
- **Data type:** `string`, `number`, `list of strings` or `list of numbers`
The select widget allows you to pick a string value from a dropdown menu.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| -------- | ----------------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| options | list of strings<br />\| list of numbers<br />\| object of `label` and `value` | | <ul><li>`string` or `number` - The dropdown displays the value directly</li><li>object with `label` and `value` fields - The label displays in the dropdown and the value saves in the file</li></ul> |
| default | string<br />\| number<br />\| list of string<br />\| list of number | `''` or `[]` | _Optional_. The default value for the field. Accepts a string. Defaults to an empty array if `multiple` is `true` |
| multiple | boolean | `false` | _Optional_. Allow multiple values/options to be selected |
| min | number | | _Optional_. Minimum number of items. Ignored if **multiple** is `false` |
| max | number | | _Optional_. Maximum number of items; ignored if **multiple** is `false` |
## Examples
### Options As Strings
<CodeTabs>
```yaml
name: align
label: Align Content
widget: select
options: ['left', 'center', 'right']
```
```js
name: 'align',
label: 'Align Content',
widget: 'select',
options: ['left', 'center', 'right'],
```
</CodeTabs>
Selecting the `center` option, will save the value as:
```yaml
align: center
```
### Options As Numbers
<CodeTabs>
```yaml
name: align
label: Align Content
widget: select
options: [1, 2, 3]
```
```js
name: 'align',
label: 'Align Content',
widget: 'select',
options: [1, 2, 3],
```
</CodeTabs>
Selecting the `2` option, will save the value as:
```yaml
align: 2
```
### Options As Objects
<CodeTabs>
```yaml
name: airport-code
label: City
widget: select
options:
- label: Chicago
value: ORD
- label: Paris
value: CDG
- label: Tokyo
value: HND
```
```js
name: 'airport-code',
label: 'City',
widget: 'select',
options: [
{
label: 'Chicago',
value: 'ORD'
},
{
label: 'Paris',
value: 'CDG'
},
{
label: 'Tokyo',
value: 'HND'
},
],
```
</CodeTabs>
Selecting the `Chicago` option, will save the value as:
```yaml
airport-code: ORD
```
### Multiple
<CodeTabs>
```yaml
name: 'tags'
label: 'Tags'
widget: 'select'
multiple: true
options: ['Design', 'UX', 'Dev']
default: ['Design']
```
```js
name: 'tags',
label: 'Tags',
widget: 'select',
multiple: true,
options: ['Design', 'UX', 'Dev'],
default: ['Design'],
```
</CodeTabs>
### Min and Max
<CodeTabs>
```yaml
name: 'tags'
label: 'Tags'
widget: 'select'
multiple: true
min: 1
max: 3
options: ['Design', 'UX', 'Dev']
default: ['Design']
```
```js
name: 'tags',
label: 'Tags',
widget: 'select',
multiple: true,
min: 1,
max: 3,
options: ['Design', 'UX', 'Dev'],
default: ['Design'],
```
</CodeTabs>

View File

@ -0,0 +1,36 @@
---
group: Widgets
title: String
weight: 24
---
- **Name:** `string`
- **UI:** Text input
- **Data type:** `string`
The string widget translates a basic text input to a string value. For larger textarea inputs, use the text widget.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ------- | ------ | ------- | ------------------------------------------------------------- |
| default | string | `''` | _Optional_. The default value for the field. Accepts a string |
## Example
<CodeTabs>
```yaml
name: title
label: Title
widget: string
```
```js
name: 'title',
label: 'Title',
widget: 'string',
```
</CodeTabs>

View File

@ -0,0 +1,36 @@
---
group: Widgets
title: Text
weight: 25
---
- **Name:** `text`
- **UI:** Textarea
- **Data type:** `string`
The text widget takes a multiline text field and saves it as a string. For shorter text inputs, use the string widget.
## Widget options
For common options, see [Common widget options](/docs/widgets#common-widget-options).
| Name | Type | Default | Description |
| ------- | ------ | ------- | ------------------------------------------------------------- |
| default | string | `''` | _Optional_. The default value for the field. Accepts a string |
## Example
<CodeTabs>
```yaml
name: description
label: Description
widget: text
```
```js
name: 'description',
label: 'Description',
widget: 'text',
```
</CodeTabs>

View File

@ -0,0 +1,66 @@
---
group: Widgets
title: Overview
weight: 0
---
Widgets define the data type and interface for entry fields. Static CMS comes with several built-in widgets. Click the widget names in the sidebar to jump to specific widget details. You can also [create your own](/docs/custom-widgets)!
Widgets are specified as collection fields in the Static CMS `config` file. Note that [YAML syntax](https://en.wikipedia.org/wiki/YAML#Basic_components) allows lists and objects to be written in block or inline style, and the code samples below include a mix of both.
To see working examples of all of the built-in widgets, try making a 'Kitchen Sink' collection item on the [CMS demo site](https://static-static-cms-demo.netlify.app). (No login required: click the login button and the CMS will open.) You can refer to the demo [configuration code](https://github.com/StaticJsCMS/static-cms/blob/main/dev-test/config.yml) to see how each field was configured.
## Available Widgets
| Name | Description |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| [Boolean](/docs/widget-boolean) | The boolean widget translates a toggle switch input to a true or false value |
| [Code](/docs/widget-code) | The code widget provides a code editor (powered by Codemirror) with optional syntax awareness |
| [Color](/docs/widget-color) | The color widget translates a color picker to a color string |
| [Datetime](/docs/widget-datetime) | The datetime widget translates a datetime picker to a datetime string |
| [File](/docs/widget-file) | The file widget allows editors to upload a file or select an existing one from the media library |
| [Hidden](/docs/widget-hidden) | Hidden widgets do not display in the UI |
| [Image](/docs/widget-image) | The file widget allows editors to upload a file or select an existing one from the media library |
| [List](/docs/widget-list) | The list widget allows you to create a repeatable item in the UI which saves as a list of widget values |
| [Map](/docs/widget-map) | The map widget allows you to edit spatial data using an interactive map |
| [Markdown](/docs/widget-markdown) | The markdown widget provides a full fledged text editor allowing users to format text with features such as headings and blockquotes |
| [Number](/docs/widget-number) | The number widget uses an HTML number input |
| [Object](/docs/widget-object) | The object widget allows you to group multiple widgets together, nested under a single field |
| [Relation](/docs/widget-relation) | The relation widget allows you to reference items from another collection |
| [Select](/docs/widget-select) | The select widget allows you to pick a string value from a dropdown menu |
| [String](/docs/widget-string) | The string widget translates a basic text input to a string value |
| [Text](/docs/widget-text) | The text widget takes a multiline text field and saves it as a string |
## Common widget options
The following options are available on all fields:
| Name | Type | Default | Description |
| -------- | ----------------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string | | The name of the field |
| widget | string | `'string'` | _Optional_. The type of widget to render for the field |
| label | string | `name` | _Optional_. The display name of the field |
| required | boolean | `true` | _Optional_. Specify as `false` to make a field optional |
| hint | string | | _Optional_. Adds helper text directly below a widget. Useful for including instructions. Accepts markdown for bold, italic, strikethrough, and links. |
| pattern | string | | _Optional_. Adds field validation by specifying a list with a [regex pattern](https://regexr.com/) and an error message; more extensive validation can be achieved with [custom widgets](/docs/custom-widgets/#advanced-field-validation) |
| i18n | boolean<br />\|'translate'<br />\|'duplicate'<br />\|'none' | | _Optional_. <img src="https://img.shields.io/badge/-Beta%20Feature-blue" alt="Beta Feature. Use at your own risk" title="Beta Feature. Use at your own risk" /><ul><li>`translate` - Allows translation of the field</li><li>`duplicate` - Duplicates the value from the default locale</li><li>`true` - Accept parent values as default</li><li>`none` or `false` - Exclude field from translations</li></ul> |
| comment | string | | _Optional_. Adds comment before the field (only supported for yaml) |
### Example
<CodeTabs>
```yaml
name: title
label: Title
widget: string
pattern: ['.{12,}', 'Must have at least 12 characters']
```
```js
name: 'title',
label: 'Title',
widget: 'string',
pattern: ['.{12,}', 'Must have at least 12 characters'],
```
</CodeTabs>

View File

@ -0,0 +1,248 @@
---
group: Contributing
title: Writing Style Guide
weight: 30
---
_Adapted from the [Kubernetes Style Guide](https://kubernetes.io/docs/contribute/style/style-guide)_
## Documentation Formatting Standards
### Use angle brackets for placeholders
Use angle brackets for placeholders. Tell the reader what a placeholder represents.
1. Display information about a cli command:
```bash
npm install <package-name>
```
where `<package-name>` is the name of a package.
### Use bold for user interface elements
> Do: Click **Save**.
> Don't: Click "Save".
_____
> Do: Select **Log Out**.
> Don't: Select 'Log Out'.
### Use italics to define or introduce new terms
> Do: A _collection_ is a set of entries …
> Don't: A "collection" is a set of entries …
_____
> Do: These components form the _control pane_.
> Don't: These components form the **control pane**.
### Use code style for filenames, directories, and paths
> Do: Open the `config.yaml` file.
> Don't: Open the config.yaml file.
_____
> Do: Go to the `/docs/guides` directory.
> Don't: Go to the /docs/guides directory.
_____
> Do: Open the `/admin/index.html` file.
> Don't: Open the /admin/index.html file.
### Use the international standard for punctuation inside quotes
> Do: Branch names begin with "cms".
> Don't: Branch names begin with "stage."
_____
> Do: The copy is called a "fork".
> Don't: The copy is called a "fork."
## Inline code formatting
### Use code style for inline code and commands
For inline code in an HTML document, use the `<code>` tag. In a Markdown document, use the backtick (`).
> Do: The `yarn start` command starts the development server.
> Don't: The "yarn start" command starts the development server.
_____
> Do: For a production build, use `yarn build`.
> Don't: For a production build, use "yarn build".
_____
> Do: Enclose code samples with triple backticks. `(```)`
> Don't: Enclose code samples with any other syntax.
### Use code style for object field names
> Do: Set the value of the `media_folder` field in the configuration file.
> Don't: Set the value of the "media_folder" field in the configuration file.
_____
> Do: The value of the `name` field is a string.
> Don't: The value of the "name" field is a string.
### Use normal style for string and integer field values
For field values of type string or integer, use normal style without quotation marks.
> Do: Set the value of `imagePullPolicy` to Always.
> Don't: Set the value of `imagePullPolicy` to "Always".
_____
> Do: Set the value of `image` to nginx:1.8.
> Don't: Set the value of `image` to `nginx:1.8`.
_____
> Do: Set the value of the `replicas` field to 2.
> Don't: Set the value of the `replicas` field to `2`.
## Code snippet formatting
### Don't include the command prompt
> Do: yarn start
> Don't: $ yarn start
## Content best practices
This section contains suggested best practices for clear, concise, and consistent content.
### Use present tense
> Do: This command starts a proxy.
> Don't: This command will start a proxy.
Exception: Use future or past tense if it is required to convey the correct meaning.
### Use active voice
> Do: You can explore the API using a browser.
> Don't: The API can be explored using a browser.
_____
> Do: The YAML file specifies the collection name.
> Don't: The collection name is specified in the YAML file.
_____
Exception: Use passive voice if active voice leads to an awkward construction.
### Use simple and direct language
Use simple and direct language. Avoid using unnecessary phrases, such as saying "please."
> Do: To create an entry, …
> Don't: In order to create an entry, …
_____
> Do: See the configuration file.
> Don't: Please see the configuration file.
_____
> Do: View the fields.
> Don't: With this next command, we'll view the fields.
### Address the reader as "you"
> Do: You can create a Deployment by …
> Don't: We'll create a Deployment by …
_____
> Do: In the preceding output, you can see…
> Don't: In the preceding output, we can see …
### Avoid Latin phrases
Prefer English terms over Latin abbreviations.
> Do: For example, …
> Don't: e.g., …
_____
> Do: That is, …
> Don't: i.e., …
_____
Exception: Use "etc." for et cetera.
## Patterns to avoid
### Avoid using "we"
Using "we" in a sentence can be confusing, because the reader might not know whether they're part of the "we" you're describing.
> Do: Version 1.4 includes …
> Don't: In version 1.4, we have added …
_____
> Do: Static CMS provides a new feature for …
> Don't: We provide a new feature …
_____
> Do: This page teaches you how to use Widgets.
> Don't: In this page, we are going to learn about Widgets.
### Avoid jargon and idioms
Some readers speak English as a second language. Avoid jargon and idioms to help them understand better.
> Do: Internally
> Don't: Under the hood, …
_____
> Do: Create a new cluster.
> Don't: Turn up a new cluster.
### Avoid statements about the future
Avoid making promises or giving hints about the future. If you need to talk about an alpha feature, put the text under a heading that identifies it as alpha information.
### Avoid statements that will soon be out of date
Avoid words like "currently" and "new." A feature that is new today will not be new in a few months.
> Do: In version 1.4, …
> Don't: In the current version, …
_____
> Do: The Federation feature provides …
> Don't: The new Federation feature provides …

View File

@ -0,0 +1,50 @@
{
"title": "Open source content management for your Git workflow",
"subtitle": "Use Static CMS with any static site generator for a faster and more flexible web project",
"get_started": {
"title": "Get Started",
"url": "/docs/start-with-a-template/"
},
"overviews": [
{
"title": "Static + content management = ♥",
"description": "Get the speed, security, and scalability of a static site, while still providing a convenient editing interface for content."
},
{
"title": "An integrated part of your Git workflow",
"description": "Content is stored in your Git repository alongside your code for easier versioning and the option to handle content updates directly in Git."
},
{
"title": "An extensible CMS built on React",
"description": "Static CMS is built as a single-page React app. Create custom-styled previews, UI widgets, and editor plugins or add backends to support different Git platform APIs."
}
],
"call_to_action": {
"title": "Getting started is simple and free.",
"subtitle": "Choose a template that's pre-configured with a static site generator and deploys to a global CDN in one click.",
"button_text": "Get Started",
"url": "/docs/start-with-a-template/"
},
"features_intro": {
"title": "A CMS that developers and content editors can agree on",
"subtitle1": "You get to implement modern front end tools to deliver a faster, safer, and more scalable site.",
"subtitle2": "Editors get a friendly UI and intuitive workflow that meets their content management requirements."
},
"features": [
{
"image": "/img/editor-friendly-user-interface.svg",
"title": "Editor-friendly user interface",
"description": "The web-based app includes rich-text editing, real-time previews, and drag-and-drop media uploads."
},
{
"image": "/img/your-content-your-way.webp",
"title": "Your content, your way",
"description": "Static CMS can integrate with most major static site generators and git repository providers."
},
{
"image": "/img/instant-access-without-github-account.svg",
"title": "Instant access without GitHub account",
"description": "With Git Gateway, you can add CMS access for any team member — even if they don't have a GitHub account."
}
]
}

42
docs/content/menu.json Normal file
View File

@ -0,0 +1,42 @@
{
"menu": {
"docs": [
{
"name": "Intro",
"title": "Getting Started"
},
{
"name": "Migration",
"title": "Migration Guides"
},
{
"name": "Backends",
"title": "Backends"
},
{
"name": "Collections",
"title": "Collections"
},
{
"name": "Widgets",
"title": "Widgets"
},
{
"name": "Media",
"title": "Media"
},
{
"name": "Guides",
"title": "Platform Guides"
},
{
"name": "Customization",
"title": "Customizing Static CMS"
},
{
"name": "Contributing",
"title": "Community"
}
]
}
}

View File

@ -0,0 +1,9 @@
{
"releases": [
{
"date": "2022-11-30T00:00:00.000Z",
"version": "1.0.0",
"description": "The first major release of Static CMS with an all-new UI, revamped documentation and much more."
}
]
}