Jeem: a tiny flat-file CMS

Jeem is a tiny flat-file CMS. No database to mess with, no admin interface, no special syntax, twigs, plugins, or anything fancy. Simply create a file for each page and write your html/Markdown!

Jeem is free for non-commercial use, and while it's in beta.


Jeem is tiny and simple; in less than 400 lines of php code, it is lightweight and fast.


Creating and editing pages is a matter of creating and editing html files. This page is all the documentation you will need!

  HTML, Markdown, and PHP

What you write is what you get. The HTML is the language of the web, and of Jeem pages as well. Jeem also supports Markdown and PHP.

  PHP Templates

PHP is a templating language in the first place, so why not use it as such?

  Open Source

Jeem is publicly available on BitBucket. You are welcome to contribute.

Installing Jeem

Download Jeem and upload the extracted files to your server. Now you have a live website with sample pages!

Since Jeem is a file-based CMS, there is no database to setup or configure. Backup, restoration, and deployment are simpler since your website is just a collection of files.

Typically you will want to create and edit your website on your local machine and then upload it to your live server. You can use PHP's built-in server for local development. To upload your pages to your website, you can use an FTP client for example.

Creating Pages

You will need to create a directory called 'pages' containing an index page and a 404 (not found) page. Until you create it, Jeem will serve pages from the 'sample-pages' directory.

The name of a page file determines its "slug", or the link which can be used to access the page. The file name has 3 parts: An ordinal, a title (or slug), and a file extension, all separated by dots . Consider this example:


The ordinal part is optional and does not affect the page link, but can be used to influence the order in which pages are listed, because Jeem always lists pages based on the alphabetical order of page file names. It can contain dots.

The title or slug part determines the link which can be used to access the page, as well as the page title if you don't explicitly specify a title.

Jeem supports 3 file extensions for page files: html, md (Markdown), and php. Code in .php pages is executed when the page is displayed. Support for .php pages is one of the main advantages of using Jeem over a static site generator.

The directory structure in the "pages" directory is reflected in the page links. If a subdirectory does not contain an index file, Jeem ignores it. Consider this table:

\n"; } ?>
Page File Page Link

Page Properties

You can assign any property you want to a page in the optional page properties section, then use those properties in your page template or the page itself. The properties section is enclosed by two tokens (default is ---). Example:

menu = Jeem
title = Jeem: the Tiny Flat-file CMS
date = July 12th, 2018

Jeem understands the following page properties:

  • menu: If not explicitly defined, Jeem sets it from the file name. The default theme uses it as the text for links to the page in navigation.
  • title: If not explicitly defined, Jeem sets it from the contents of the first <h1> tag. If no <h1> tag was found, it is set to the 'menu' property. The default theme uses the page title property to set the <title> tag in the page head.
  • description: If defined, the default theme uses it to create a meta description tag inside the <head> tag, which is useful for search engine visibility.
  • theme: Defines the theme to use for the page. This overrides the theme you specify in the site's configuration file (jeem-config.php, see Configuring Jeem below).
  • hidden: If set to true, the page will not show up in navigation links, but will remain accessible through direct links.
  • private: If set to true, the page will not be accessible. Trying to access it will show a "This page is private" page. Implies 'hidden'.
  • redirect: slug of a page to redirect to when this page is visited. Note that Jeem does nothing to ensure you have no cyclic redirects.

Jeem Values

Inside your page and theme, you can insert a jeem value from the "Name" column inside double curly braces {{ }} (no spaces) and Jeem will substitute it with the actual value.

{{page.title}}Jeem: the tiny flat-file PHP CMS
{{JEEM_VERSION}}1.0 - beta

PHP Pages

One of the main advantages of using a dynamic CMS instead of a static site generator is the ability to create "dynamic" sites. Jeem supports php pages and themes for this purpose.

Jeem needs to read a page file for two reasons: extracting properties (for generating navigation links for example), and to actually display the page. Jeem does not execute php pages in the first case - it only executes them when they are being displayed.

Configuring Jeem

You can set custom configuration settings for Jeem by creating a file called "jeem-config.php" in the root directory. Use the sample-jeem-config.cfg file as reference. Uncomment the setting you want and modify its value:


Currently Jeem comes with two themes (or templates). You can select the active theme by changing $config["theme"] = "default"; in jeem-config.php. You are encouraged to create your own theme however. To start, copy the "bare" or "default" directory in the "themes" directory, then rename it (Jeem uses the directory name as the theme name) and start modifying it. Jeem only requires an "index.php" file that will be the page template.

Clean URLs

You can enable clean urls in Jeem using the included .htaccess file for apache servers. This will allow you to access pages without the "?page=" part of the URL. If Jeem does not automatically detect that url rewriting is enabled, try uncommenting the line $_ENV["JEEM_CLEAN_URLS"] = true; in jeem-config.php.

Questions and Answers

Creating a Blog

You can easily create a blog on your website thanks to the hierarchical nature of the file system. The screenshot shows how you would store your blog inside the "pages" directory.

The ordinal for each blog post contains the post date in yyyy-mm-dd format to ensure that blog posts show up in chronological order.

Some basic php knowledge is required to write the logic needed to list blog posts on the blog index page. You can use the included sample blog as reference.

Basic example: pages/blog/index.php

foreach( GetPages( $page["dir"], 1 ) as $post )
	if( $page["url"] == $post["url"] )
		continue; // skip this index page.
	<div class="post">
			<a href="<?=$post["url"]?>"><?=$post["title"]?></a>
			<div class="date"><?php echo $post['date']; ?></div>
		<div class="excerpt">
			<?php if( isset($post["excerpt"]) ) echo $post["excerpt"]; ?>
<?php }?>

A similar setup can be used to group any set of related pages together, like news items for example. See the included sample pages.

Jeem does not implement pagination yet.

blog file structure
Typical file structure for a website with a blog

More Information

Visit for more information about Jeem.