$this->app->make('widget_types')->register('example.latest', [
'label' => 'Latest examples',
'group' => 'Example',
'module' => 'example',
'render' => fn (array $w, ?array $viewer, bool $dark): string
=> $this->latestHtml($w),
]);And an area, if your pages should accept widgets:
$this->app->make('widget_areas')->register('example.side', [
'label' => 'Beside the examples',
'hook' => 'example.index.side',
'module' => 'example',
]);Then @hook('example.index.side') in the template.
🚨 Both in register(), never boot(). The widget module wires a listener per area during its boot, so an area registered in boot() only works if your module happens to boot first.
🚨 Filter by the viewer's groups inside your render. A widget renders on pages the operator is not thinking about, which makes it the easiest place in the product to leak something — and filter before the limit, or the widget quietly shows five rows instead of ten.
Return an empty string to draw nothing. The renderer handles the arranging case.