Timberdocs

Developers

Architecture

The big picture#

browser → web server (.htaccess / nginx) → index.php        (public site)
                                         → padmin/index.php (admin)
                 both load inc/bootstrap.php: storage, settings, sessions, CSRF, auth

1. Web server layer#

The root .htaccess (or the nginx config) does five things, in order:

  1. Blocks internals: data/, inc/, templates/, themes/, any _folder, dotfiles, README.md.
  2. Allows PHP only for two front controllers (index.php, padmin/index.php). PHP in uploads/ and assets/ never runs.
  3. Serves existing files directly (CSS, JS, images, a generated sitemap.xml, this docs/ site).
  4. Routes /padmin… to the admin.
  5. Routes everything else to index.php.

2. The installer gate#

timber_installed() is true when data/users.json has at least one account and data/settings.json exists. Until then, both front controllers redirect to /padmin/install, which writes settings, menus, three starter versions, sample content, forms and your first Administrator account via timber_install().

3. The public front controller#

In order:

  1. /v/{slug}/… version previews (live/preview status, a logged-in admin, or the share key). noindex, no-store.
  2. Redirects, then maintenance mode (503 for visitors).
  3. A live version must exist.
  4. The route table:
RouteHandler
/The page chosen as homepage, else the live version's home with the latest posts
blog, blog/{slug}, partials/postsBlog index, post, "load more" fragment
a form's path, forms/submit/{key}Form page and submission
sitemap.xml, feed.xml, robots.txt, favicon.icoGenerated live
{page slug}A published page
anything elseBranded 404

4. Rendering#

partial($template, $vars)   // include a template, extract($vars, EXTR_SKIP)
render($template, $vars)    // buffer a partial into $content, then wrap it in layout
section($key)               // render one homepage section of the current version
region('header'|'footer')   // a Global block, else the theme's parts/…

Lookup order: themes/{current theme}/{name}.php, then templates/{name}.php — which is how a theme inherits the shared page/post/form templates and overrides only what it needs.

The current version is the live one unless the request is a /v/{slug} preview; vc(), href() and theme_asset() all read from it, so live and preview render identically.

5. The admin#

/padmin/{section}/{action}/{id} → a $routes map → padmin/views/{file}.php. Each view is controller and template in one file: handle actions at the top, render with admin_page() at the bottom. htmx handles saves and toasts; Alpine handles editors and drag-sort. See Extending Timber.

6. Dates and time#

UTC everywhere; ISO 8601 strings in storage. A post dated in the future is scheduled: it's hidden until then, no cron required.

Read more#

Timber is open source. Built by indies, for indies. © 2026