UI render hooks
Named injection points in core Blade templates. Register a callback that returns HTML — it's inserted in place on every render.
UI render hooks are extension points baked into core Blade templates. A plugin registers a callback with Hook::add(); core calls Hook::render() at the named point and injects the concatenated output.
This is separate from action and filter hooks — it's specifically for injecting HTML into the admin or frontend UI without overriding templates.
How to register
use Contensio\Support\Hook;
Hook::add(string $name, callable $callback, int $priority = 10): void
The callback must return a string. Return '' to render nothing conditionally.
How it works in templates
Core templates call:
{!! \Contensio\Support\Hook::render('hook.name') !!}
{!! \Contensio\Support\Hook::render('hook.name', $arg) !!}
All registered callbacks run in priority order; their output is concatenated and rendered raw (unescaped).
contensio/admin/login-after-form
Location: Directly below the username/password form on /login
Arguments: none
Since: 1.0.0
Source: core/resources/views/auth/login.blade.php
Use this point to add alternate sign-in options — social login buttons, magic links, SSO entry points.
Hook::add('contensio/admin/login-after-form', function (): string {
return view('my-plugin::partials.login-buttons')->render();
});
Notes
- Renders on every login page load, including after a failed attempt.
- Multiple plugins can register — output is concatenated in priority order.
- The output sits inside a narrow single-column layout. Use utility classes that fit a column (avoid full-width grids).
contensio/admin/profile-sections
Location: Below the form on the admin user profile page (/admin/profile)
Arguments: $user — the currently authenticated user model
Since: 1.0.0
Source: core/resources/views/admin/profile/index.blade.php
Use this point to add extra sections to the admin profile page — connected accounts, API tokens, notification preferences.
Hook::add('contensio/admin/profile-sections', function ($user): string {
if (! $user) return '';
return view('my-plugin::partials.profile-section', compact('user'))->render();
});
Notes
$useris the authenticated admin user. Always guard againstnull.- Renders inside the main profile card column. Match the visual style of existing profile sections — use
<div class="border-t border-gray-100 pt-6 mt-6">as a separator.
contensio/admin/settings-cards
Location: Inside the grid on the Configuration hub (/admin/settings)
Arguments: none
Since: 1.0.0
Source: core/resources/views/admin/settings/index.blade.php
Use this point to add your plugin's settings card to the central Configuration hub, so users can find your settings in the same place as all other settings.
Hook::add('contensio/admin/settings-cards', function (): string {
return view('my-plugin::partials.settings-card')->render();
});
Example card template
{{-- my-plugin::partials.settings-card --}}
<a href="{{ route('my-plugin.settings') }}"
class="block bg-white border border-gray-200 rounded-lg p-5 hover:border-gray-300 transition-colors group">
<div class="flex items-center gap-3 mb-2">
<div class="w-8 h-8 bg-blue-50 rounded flex items-center justify-center">
<i class="bi bi-gear text-blue-500"></i>
</div>
<h3 class="font-semibold text-gray-900">My Plugin</h3>
</div>
<p class="text-sm text-gray-500">Configure My Plugin settings.</p>
</a>
Notes
- The hub uses a responsive CSS grid (
grid-cols-1 md:grid-cols-2 lg:grid-cols-3). Your card should be a single grid cell — a link to your dedicated settings page, not the settings form itself. - Keep the card concise: icon, title, one-line description.
contensio/admin/dashboard-quick-actions
Location: Header action bar of the admin dashboard (/admin)
Arguments: none
Since: 1.0.0
Source: core/resources/views/admin/dashboard/index.blade.php
Use this point to add shortcut buttons to the dashboard header — quick-create links, import triggers, or any action your plugin exposes to editors.
Hook::add('contensio/admin/dashboard-quick-actions', function (): string {
return '<a href="' . route('my-plugin.create') . '"
class="inline-flex items-center gap-2 px-4 py-2 bg-white border border-gray-200
rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors">
<i class="bi bi-plus-lg"></i> New Item
</a>';
});
Notes
- Renders in the top-right area of the dashboard alongside any core quick-action buttons.
- Output should be one or more
<a>or<button>elements styled as small action links.
contensio/admin/dashboard-stats
Location: Below the core stat cards row on the admin dashboard
Arguments: none
Since: 1.0.0
Source: core/resources/views/admin/dashboard/index.blade.php
Use this point to inject additional stat cards after the built-in row (posts, pages, media, comments). Matches the existing card grid.
Hook::add('contensio/admin/dashboard-stats', function (): string {
$count = \MyPlugin\Models\Item::count();
return view('my-plugin::partials.dashboard-stat', compact('count'))->render();
});
Notes
- Core uses a responsive stat card grid. Return one or more card
<div>elements matching the core card style so they fit visually. - Keep queries fast — this fires on every dashboard load.
contensio/admin/dashboard-widgets
Location: Between the content panels (recent posts, recent comments) and the activity log on the admin dashboard
Arguments: none
Since: 1.0.0
Source: core/resources/views/admin/dashboard/index.blade.php
Use this point to inject full-width or half-width panels — summaries, charts, approval queues, or any at-a-glance data your plugin manages.
Hook::add('contensio/admin/dashboard-widgets', function (): string {
$items = \MyPlugin\Models\Item::latest()->limit(5)->get();
return view('my-plugin::partials.dashboard-panel', compact('items'))->render();
});
Notes
- The dashboard at this point is full-width. Your panel can use the full container or be wrapped in a grid alongside other injected panels.
- Return
''if there is nothing to show (e.g., no items yet) rather than rendering an empty panel.
contensio/admin/dashboard-after
Location: Below the activity log — the very bottom of the admin dashboard
Arguments: none
Since: 1.0.0
Source: core/resources/views/admin/dashboard/index.blade.php
Use this point for secondary or supplementary content that should not compete with the main dashboard area — onboarding checklists, upgrade notices, plugin-specific activity feeds.
Hook::add('contensio/admin/dashboard-after', function (): string {
if (MyPlugin\Support\Config::get('setup_complete')) return '';
return view('my-plugin::partials.setup-checklist')->render();
});
Notes
- Fires after all other dashboard content. Suitable for lower-priority information.
- Onboarding or setup notices are a good fit here; they don't interrupt the main workflow.
Checking if a hook has callbacks
if (Hook::has('contensio/admin/login-after-form')) {
// at least one callback is registered
}
Useful for wrapping hook output in a container that should only render when there's something to show:
@if(\Contensio\Support\Hook::has('contensio/admin/login-after-form'))
<div class="mt-6 border-t border-gray-100 pt-6">
{!! \Contensio\Support\Hook::render('contensio/admin/login-after-form') !!}
</div>
@endif
Error handling
If a callback throws, the exception is reported via report() and that callback's output is skipped. Other registered callbacks continue to run. The page renders normally.
See also
- Hook system guide — actions, filters, and full API
- Contensio hooks vs WordPress hooks