Skimpy is a file based CMS that is written with and runs on PHP (Lumen). It is NOT a static site generator. Generators are cumbersome tools. Skimpy is easy to use. You just create a file, and there it is! Skimpy is so easy to use that it might be the easiest CMS/blogging tool that you've ever tried. Don't believe me? Give it a shot!
- Introduction
- Installation
- Creating a Blog Post
- Adding Front Matter
- Creating a Page
- Categorizing/Tagging Content
- Creating Page Types
- Index Pages
- URI Mapping & Template Variables
- Template Hierarchy
- Custom Templates
- How does Skimpy work?
Skimpy is a simple file based CMS that can be used to make a website or blog. Skimpy is built for developers, though anyone who can create files and put a PHP site on the internet can use it. Skimpy aims to be simple and easy to use.
- PHP >= 8.0.2
- OpenSSL PHP Extension
- PDO PHP Extension
- Mbstring PHP Extension
You can install Skimpy with Composer and run it with PHP's built in server or you can run it in a container with the built in Dockerfile.
composer create-project --prefer-dist skimpy/cms skimpy
cd path/to/skimpy
cp .env.example .env
- Update the .env file to match your preferences/info
php -S localhost:4000 -t public
- Visit http://localhost:4000
composer create-project --prefer-dist skimpy/cms skimpy
cd path/to/skimpy
cp .env.example .env
- Update the .env file to match your preferences/info
- Build the Container
docker build -t skimpy .
- Run the container with a volume so code changes are reflected
docker run -p 4000:80 -v $PWD:/var/www/html skimpy
- Visit http://localhost:4000
- Create a new markdown file called
test-post.md
inside thesite/content
directory. - Visit
http://localhost:4000/test-post
and voila! - Add a markdown header to the file
# Test Header
and refresh. You'll see an h1 tag that reads "Test Header".
In the example above where we created "test-post", you can see that Skimpy uses the filename as the URI to the post.
If you put the file inside a directory "products" and name the file "widget.md". The URI to the file will be /products/widget
path = path/to/skimpy/site/content/products/widget.md
URL = http://localhost:4000/products/widget
You can customize certain properties of an entry using front matter key values. All front matter is optional and Skimpy will use sensible defaults.
Always use exactly three hyphens "---" to separate front matter from content. Otherwise, you'll get an exception or your file won't parse correctly.
Keep in mind that using three hypens "---" on their own line, or at the end of the file without a new line, will cause Skimpy to assume it is the front matter separator whether that is what you intend or not.
Key | Description |
---|---|
title | Custom title if different from the filename |
date | The published date of the content. Always use PHP Y-m-d or Y-m-d H:i:s format. DATES MUST BE QUOTED "2020-01-22" |
seoTitle | The content to put in the head <title> tag |
description | The SEO meta description for this page (if any) |
template | The name of the template to use if not using whichever template matches the convention for this content type |
categories | The categories the content belongs to |
tags | The tags to assign to the content |
Any key/value you put in front matter that isn't built in will be stored as metadata.
You can access any custom front matter key values in your templates.
The "restaurants" key in the example file below is an example of metadata.
Call the "meta" method on the entry object and pass in the key you want.
{{ entry.meta('restaurants') }}
title: My 2019 Trip to Germany
date: "2019-08-27"
description: The SEO meta description (if any) goes here
seoTitle: Custom SEO title here
categories: [Vacation]
tags: [Personal Growth]
restaurants: [Kin Dee, Markthalle Neun]
---
The content for the post goes here...
Skimpy sites don't have to be blogs at all but it does make blogging easy if that's what you are doing. Before I explain "pages" in Skimpy, let's first define what a page is. Both pages and posts are just files that hold content for display when someone hits the matching URI. The only difference between a "page" and a "post" is that people don't typically display an index of pages like they do blog posts. Pages are not usually listed on the home screen with "excerpts" like blog posts and pages aren't really meant to be categorized or tagged.
So if you would like to create an "entry" on your site that doesn't show up in your default blog feed, then you want to create a page. This is accomplished by creating a subdirectory under the content directory and placing any files that you want to be a "page" inside that directory. You can call the directory anything you want but keep in mind the directory name will be part of the URI to access the page.
You may also exclude root entries from displaying on the home page or an index by manually changing the content type via the Front Matter "type" key. This essentially is the same as placing the file in a subdirectory. Any file inside a subdirectory has a "type" property with a value matching the name of the parent directory. Try and avoid using the "type" key if you can. Sites that follow the conventions will be more well organized and easier to navigate. You can always just make a "pages" directory and put your pages there. Having a URI segment "pages" precede the page URI really isn't a big deal.
Content can be categorized or tagged. Categories and tags are referred to as "taxonomies" (like in WordPress).
Taxonomies are a way of classifying content. There are already two predefined taxonomies in the default
Skimpy installation. And you guessed it, those taxonomies are categories and tags. If you wanted to add your own
taxonomy, you would just copy the categories.yaml
file and change the values.
Taxonomies have "terms". In the case of the "categories" taxonomy, the terms are the actual categories.
You add new categories by adding a new array to the terms key in the content/taxonomies/categories.yaml
file. You simply provide a name for the category and a slug to be used in the URI.
By default, Skimpy will list links to the terms (category names)
you have defined in the categories.yaml
file, when you visit
/categories
. If you want to turn this off for a particular taxonomy
(like categories) then you should set the has_public_terms_route
yaml key to false
in categories.yaml
.
You simply add the key categories
to the YAML front matter of any content file and provide an array of category names. Use the actual "name" NOT the slug.
Don't forget the Front Matter separator! "---"
categories: [Web Development]
tags: [Tag 1]
date: "2020-01-21"
---
Your post content here...
Let's say you want to add a page for every person on your team and you want to list those people on your "team" page.
- Create folder
content/team
- Create file
content/team/kevin-rose.md
- Create file
content/team/tim-ferriss.md
- Enable a "team index" by creating
content/team/index.md
-
The following URLs are now valid
example.com/team
- Lists links to any file under the "team" directoryexample.com/team/kevin-rose
example.com/team/tim-ferriss
Index pages are basically an archive of content files inside of a subdirectory of content.
See the example for creating page types
By default, there is no index for files inside a subfolder of the content directory.
To turn the index uri on, in other words, to make the subfolder name a valid URI on your
website, you just create a file inside the subdirectory called index.md
There are three types of "entities" in Skimpy - entry
, taxonomy
, and term
.
The current URI determines what type of entity is queried and what the template variable names are.
You can determine what file is being displayed just by looking at the URI. The file structure
maps directly to the current URI. Just keep in mind when you create a taxonomy file, you are
creating a URL that matches the name of that file. When you create a directory inside content and you place
an index.md file in it, you are creating a URL that matches the path to that directory.
When URI Matches | You'll See | Template Variable |
---|---|---|
the name of a content file | The files markdown content | entry |
the name of a taxonomy file (like "categories") | A listing of links to the terms registered to the taxonomy | taxonomy |
the name of a term (like a category name) | A listing of links to entries with that term assigned in their Front Matter | term |
Which template is used depends on the type of entity (entry, taxonomy, term) being displayed. You can manually set the template with the template
front matter key
- FrontMatter key
template: your-template-name
- The parent folder name if the file is in a subdirectory of content
- If the file is an index (index.md), the index template.
- The entity type (entry = entry.twig, taxonomy = taxonomy.twig, term = term.twig)
Skimpy has some conventions for deciding what template to use. You can override the conventions
by adding a template
key to the front matter of a content file.
- Create the template
site/templates/my-custom-template.twig
-
Open or create a content file and set the template in the Front Matter.
template: my-custom-template
Deploying Skimpy to production is easy. Just make sure your server meets the server requirements and create a virtual host with the document root set to the skimpy public folder.
What is auto rebuild? Auto rebuild refers to Skimpy scanning your content folder and updating the SQLite DB on every request to your website. This makes development quick and easy. You don't have to worry about "generating" your website. You can turn this feature on or off in your local or production environment. I wouldn't really worry about it too much because it's not going to make much of a difference. That being said I will share my preferred way of deploying with Skimpy.
-
I track my SQLite DB with Git so it gets automatically deployed when I push to master. This allows my Skimpy sites to be updated to latest when I push to master without the production sites having to rebuild the database on each request
-
I set
AUTO_REBUILD=false
in my.env
file on the production site so the database won't be rebuilt on every request.
When a request hits your website, Skimpy scans all of the files in your site/content
directory, converts them to
Doctrine entities, and shoves them into an sqlite database. You don't have to pay any
attention to the database at all if you don't want. The reason Skimpy converts your content into database records is so that it can take
advantage of all of the power of doctrine and SQL in general. The database component in Skimpy is used more like a cache.
You should never be editing your database directly as Skimpy will just wipe out any changes you made automatically the next time your website receives a request.
Skimpy uses Doctrine and a database for several reasons. The primary reason for the DB is so that you don't have to "generate" your actual website everytime you make changes to your content, or run a "watch" command that generates the changes when you save a file. Doing things the "generater way" is just nonsense. Generating adds complexity to creating a website or blog. Writing and adding content should be mindless and that's what Skimpy sets out to accomplish.
Skimpy CMS is open-sourced software licensed under the MIT license.