Croft

A small PHP CMS

Declare the content.
The CMS builds the rest.

One file describes a kind of content. From it you get the database table, the admin list, the editor, publishing, revision history and the public routes — with no migration to write and no screens to build.

Read the guides What it is not

Plain PHP 8.1+ and SQLite. No framework, no Composer, no npm, no build step.

The whole idea

One file per kind of content

Save this as app/types/recipe.php and reload any page.

You now have a recipes table, Recipes in the admin menu, a list screen, an editor with a markdown toolbar and live preview, draft and scheduled publishing, per-item SEO, revision history with restore, and the routes /recipes and /recipes/{slug}.

Add a field tomorrow and the column appears by itself.

app/types/recipe.php
<?php
return [
    'label'    => 'Recipe',
    'features' => ['slug', 'status', 'schedule', 'seo', 'revisions'],
    'title'    => 'name',
    'public'   => [
        'index'    => '/recipes',
        'single'   => '/recipes/{slug}',
        'template' => 'recipe',
    ],
    'fields' => [
        ['name' => 'name',   'type' => 'text',     'panel' => 'title'],
        ['name' => 'method', 'type' => 'markdown', 'panel' => 'main', 'editor' => true],
        ['name' => 'serves', 'type' => 'number',   'panel' => 'sidebar'],
        ['name' => 'photo',  'type' => 'media',    'panel' => 'sidebar'],
    ],
];
That is the entire definition. There is no second file.

What comes with it

The parts you would have written anyway

None of this is a plugin. It is in the box, and it is the same for every type you declare.

  • A real editor

    Markdown with a toolbar, live preview and word count. Media picker. Repeaters. Drag to reorder. Ctrl+S to save, and a warning if you leave with unsaved changes.

  • Publishing

    Draft and published, future-dated scheduling, featured items, per-item SEO and social image, and an automatic redirect when an address changes.

  • History

    Every save keeps the previous version. Restore any of them, including items someone deleted last week.

  • Media

    Drag-and-drop uploads, automatic 480–2400px WebP versions, and a warning before deleting a file that is still used somewhere.

  • Security

    bcrypt passwords, optional two-factor sign-in, login rate limiting, CSRF on every form, idle timeout, admin and editor roles.

  • Page sections

    Give a type a blocks field and editors assemble pages from the sections you allow — like this page, which is eight blocks in the panel.

Getting started

Four steps from download to live

  1. Upload the folder

    Point the domain at public/. There is nothing to compile and nothing to install.

  2. Create your account

    Visit /admin. It asks for the app key from config.php, then your name and a password.

  3. Declare your types

    Copy one of the two examples in app/types/ and change the fields to suit the site.

  4. Write the templates

    Plain PHP in app/views/site/. The CMS decides what data a template receives, and nothing else.

Everything on this page — the headline, these words, the order of the sections — was typed into the admin panel.
Open /admin and edit the page you are reading

Questions

The ones people actually ask

Do I need to know a framework?

No. It is plain PHP: templates, a router and about fifteen small classes. If you can read a PHP file you can change anything here.

What happens when I add a field to a type?

The column is added on the next page load and the input appears in the editor. Removing a field hides it again and leaves the data alone, so a typo never costs you content.

Why SQLite rather than MySQL?

A content site is read-heavy and small. One file is the easiest thing in the world to back up, copy and move, and it needs no database server. The data layer is one small class if you ever want to change it.

Can clients break the design?

Not easily. They fill in the fields you declared and reorder the sections you allowed. They cannot invent new kinds of section or touch the templates.

Is there a build step?

None. No npm, no Composer, no compiled assets. Upload the folder and it runs.

Have a look inside

The guides walk through declaring a type, the field types, and how the editing screen is put together.

Read the guides