# UI extension points and hooks
Use an extension point or hook when core exposes one. This keeps plugins decoupled from controller overrides and copied core views.
# UI extension points
Register a partial against a named location:
public override void Execute(IServiceCollection services)
{
services.AddUIExtension("header-nav", "/Views/Shared/ExampleNav.cshtml");
}
Core renders locations with the ui-extension-point view component. Search the target BTCPay Server version for vc:ui-extension-point to find available locations and inspect the supplied model before writing the partial. Use an absolute view path to avoid accidental view-name collisions.
Common extension points include:
| Location | Model | Typical use |
|---|---|---|
global-nav | GlobalNavViewModel | Content at the start of the global top navigation. |
global-nav-icons | GlobalNavViewModel | Compact icon actions beside notifications and other global controls. |
server-nav | MainNavViewModel | Server-administration navigation visible in the global settings menu. |
user-nav | MainNavViewModel | Account-level navigation visible in the global user menu. |
store-nav | MainNavViewModel | Store navigation entries outside a specific built-in category. |
store-category-nav | MainNavViewModel | Entries inside the store-settings category. |
header-nav | MainNavViewModel | General plugin entries in the main navigation's Plugins section. |
store-integrations-nav | MainNavViewModel | Store-specific entries in the Plugins section. |
layout-banner | None | A site-wide banner above the page body. |
dashboard | StoreDashboardViewModel | Store dashboard content above the built-in widgets. |
checkout-end | CheckoutModel | Payment-method or plugin content near the end of checkout. |
This list is intentionally not exhaustive. An extension partial must emit markup
appropriate for its location, such as an <li> for a navigation list. Inspect
the rendering view and a built-in registration using the same location to
confirm layout, permissions, and model assumptions for the BTCPay Server version
your plugin supports.
Embed static plugin resources through the plugin project and reference them with ~/Resources/... plus asp-append-version="true". Follow the plugin template (opens new window) project settings for the current resource layout.
# Action hooks
An IPluginHookAction observes or performs work at a named hook and does not replace the value:
public sealed class ExampleAction : IPluginHookAction
{
public string Hook => "example-hook";
public Task Execute(object args) => Task.CompletedTask;
}
Register it as IPluginHookAction. Search core for ApplyAction( to discover hooks and inspect the argument type at the call site.
# Filter hooks
An IPluginHookFilter receives the current value and returns the value passed to the next matching filter:
public sealed class ExampleFilter : IPluginHookFilter
{
public string Hook => "example-filter";
public Task<object> Execute(object args) => Task.FromResult(args);
}
Register it as IPluginHookFilter. Search core for ApplyFilter( to discover hooks. Preserve the documented runtime type and assume other plugins may run before or after yours. Hook names are matched case-insensitively, but use core's spelling.
Hook failures are logged by the host and processing continues. Handle expected failures yourself when silent continuation would leave your plugin inconsistent. If no suitable stable extension point exists, propose one in core rather than coupling to page markup or an internal implementation detail.