Developers
Extending Timber
Timber has no plugin API. You extend it by editing the code, which is small and readable on purpose. Here are the common extension points.
Add an admin screen#
- Create
padmin/views/yourthing.php:
<?php
if ($action === 'save' && admin_is_post()) {
save_settings('mything', ['name' => input('name')]);
notify('Saved'); // toast via HX-Trigger
exit;
}
admin_page('Your thing', function () { ?>
<form hx-post="<?= admin_url('yourthing/save') ?>" hx-swap="none" class="stack">
<?php admin_field('name', 'Name', settings('mything.name')) ?>
<button class="btn btn-primary btn-save">Save</button>
</form>
<?php }, ['section' => 'yourthing']);- Register it in
$routesinpadmin/index.php. - Add a nav entry to
$navinpadmin/views/_layout.php.
Helpers include admin_field, admin_toggle, media_field, editor_field, code_field, admin_go, notify, save_settings, unique_slug, seo_panel, status_badge and admin_icon.
Add a collection#
Pick a name and use collection() / save_record() / delete_record(). Register it in DATA_LISTS (or DATA_OBJECTS) in padmin/views/data.php so the data browser and export understand it.
Add a public route#
Add a case above the page-slug fallback in index.php, and add the first segment to the reserved slug lists (unique_slug() in padmin/lib.php, REDIRECT_RESERVED in views/redirects.php).
Add an integration#
Each integration is one small file in inc/integrations/{id}.php returning a definition: a name, the module it serves (analytics, email, notifications, support), an icon, a docs link, the fields to collect (text or secret), and an optional verify callable. See brevo.php and google-analytics.php for examples. Non-secret values go to settings; secret ones to data/secrets.json.
Add a theme#
See Building themes.
Add a content converter#
The WordPress migrator is source-agnostic after parsing: any parser that writes the same items.ndjson records can reuse the import, menu, redirect, media and undo pipeline — a good starting point for Ghost, Squarespace or Jekyll importers.
Conventions#
- Escape output with
e(); JSON inside<script>usesjson_encode(..., JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT). - Prefix new admin helpers with
admin_— shared hosts sometimes define short global function names likego(). - Build URLs with
url(),href(),asset(),media_url(),abs_url(). - Never bypass CSRF. Plain forms need a hidden
_csrffield. - Keep the three principles: no build step, no required service, settings over code.