A configurable CMS built with Svelte and inspired by Decap CMS and Sveltia CMS.
I created this project because I wanted a CMS with a rich-text markdown widget, more intuitive custom preview configuration, and a configurable backend.
[!NOTE] When using the local backend, certain filesystem interactions may perform slowly on Firefox.
For maximum compatibility, use a Chrome-based browser.
npm i snow-cms
[my-site]/cms
app
.cms-config
in your project at the same level as the previously created cms
path.[my-site]/cms-config
config.yml
and any relevant css, html template, and js files.An example of a basic project with the expected structure would look something like this:
index.html
cms/ // The path to access the CMS. (Path name can be anything)
|-- index.html // Page that loads 'cms.js'
|-- snow.js // JS module that imports the CMS. (File name can be anything)
cms-config/ // The path containing CMS config files. (Must be named 'cms-config')
|-- config.yml // CMS config file
|-- preview-template.html // Layout template to apply to page previews in the editor
|-- preview-styles.css // Styles to apply to page previews in the editor
|-- cms-actions.js // Additional behavior to hook into editor actions. (Optional)
[!NOTE] This example assumes the use of a build system that allows importing css in js. But you should adapt importing of the css and js to your project's build system.
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- The module that loads the CMS -->
<script type="module" src="snow.js"></script>
</head>
<body>
<!-- The root element used to load the CMS -->
<slot id="app"></slot>
</body>
</html>
// Import the CMS css and js
import 'snow-cms/dist/index.css';
import 'snow-cms/dist/index';
See Config for information on configuration as well as dev-site/cms-config for example config files.
npm install -g pnpm
pnpm install
pnpm run dev
Then navigate to http://localhost:5173/
in your browser to view the dev site.
pnpm run build
Built files will be output to the dist/
directory.
Contains the source files to build the CMS.
Contains files for running the development server.
Development server site's photos by Pixabay and Simon Berger from Pexels.
See dev-site/cms-config for example config files.
[!NOTE] The GitHub backend requires a GitHub App to authenticate through and a server-side handler to exchange the access token.
The GitHub app should be configured with
Callback URL
andSetup URL
set to the url of the CMS.The server-side handler should exchange the received auth code for a user access token. Using one of the Oauth App middlewares makes this easy to set up.
Widgets share the following configuration options:
label
: The input label displayed in the editor interface.name
: The name of the input.default
: The input's default value.required
: Whether the input is required. (Defaults to true
.)[!IMPORTANT] Each collection must have widgets configured for the names
title
,date
,draft
, andbody
.
widget
: 'boolean'
{
label: 'Draft',
name: 'draft',
widget: 'boolean',
required: false
}
[!NOTE] If the Boolean Widget's
required
option is set totrue
or isn't specified, the input's value must betrue
for editor data to submit.
widget
: 'number'
step
: The amount the value increments / decrements by. (Defaults to 1
.)min
: The minimum value allowed.max
: The maximum value allowed.{
label: 'Cookies',
name: 'cookies',
widget: 'number',
step: 5
}
widget
: 'datetime'
type
: 'datetime-local|date|time'
Which input to display.datetime_format
: How the datetime should be displayed.date_format
: How the date should be displayed.time_format
: How the time should be displayed.{
label: 'Publish Date',
name: 'date',
widget: 'datetime',
type: 'datetime-local',
datetime_format: 'MM.DD.YYYY HH:mm'
}
[!NOTE] The corresponding
format
option should be set depending on thetype
.
widget
: 'string|text'
Which input to display. Use string
for single-line or text
for multiline.{
label: 'Title',
name: 'title',
widget: 'string'
}
widget
: 'markdown'
{
label: 'Body',
name: 'body',
widget: 'markdown'
}
widget
: 'hidden'
type
: 'boolean|number|text|datetime-local|date|time'
The type of the input's value.default
: The value of the hidden input.{
name: 'hiddenValue',
widget: 'hidden',
type: 'text',
default: 'secret box'
}