Widget Structure
Every widget is a self-contained directory under widgets/. The minimum viable widget has two files. Additional files are optional but supported.
Directory Layout
widgets/
recent_posts/ ← directory name = slug
widget_info.json ← required: manifest
views/
widget.tpl ← required: main render template
Language/
en/
Widget.php ← optional: i18n strings
No other file types are recognized or loaded by the widget system. PHP files in a widget directory are never executed — the widget engine does not scan for or autoload them.
widget_info.json
The manifest file. Must be valid JSON. Parsed by WidgetService::discover() when discovery is triggered. Keys:
name— human-readable display name (shown in admin)slug— identifier, must match directory namedescription— short description shown in admin widget listversion— semver string, stored inwidgets.versioncolumnmin_pubvana_version,max_pubvana_version— compatibility bounds (advisory)update_url— URL polled by the auto-update system for new versionssupport_url— linked from admin widget detailauthor— credit stringadmin.options— option schema (drives the admin configuration form)output.template— template file path relative toviews/output.providers— data provider declarations (optional)
views/widget.tpl
The template rendered by WidgetService::renderArea(). Uses the Pubvana template engine syntax (.tpl files, not PHP). Receives the following variables:
| Variable | Type | Source |
|---|---|---|
options | object | Widget instance options merged with schema defaults |
cls | object | CSS class mapping from the active theme |
| (provider keys) | mixed | Results from declared data providers |
The template file name is set by output.template in the manifest. Convention is widget.tpl but any filename is valid.
Language Files
Optional. Place PHP array files at Language/en/Widget.php (or other locale codes). These are loaded by CI4's Lang service when the widget is rendered. Access strings in templates via {! lang "Widget.key" !}.
Example Language/en/Widget.php:
<?php
return [
'no_posts' => 'No posts found.',
'read_more' => 'Read More',
];
What Is NOT Supported
- PHP class files — never executed
- CSS or JS files — no asset pipeline for widgets; inline styles or theme-provided classes only
- Sub-templates (
{% include %}inside widget.tpl) — widget templates are standalone; includes reference the active theme's partials directory, not the widget directory - Widget-to-widget dependencies — each widget is isolated