Add an add-on tab to one show’s settings
Anything whose data belongs to a single show — a per-show import, a per-show API key, per-show mappings — belongs in that show’s settings rather than on the install-wide Benecaster Settings screen, where no show is in scope.
Declare the tab in PHP with benecaster_show_settings_pages, then fill its slot in JavaScript. The tab appears in the show’s settings navigation below Templates, with the puzzle-piece icon every add-on tab uses.
Install-Wide Page or Per-Show Tab
The two routes look alike and differ in every detail:
| Install-wide | Per show | |
|---|---|---|
| Filter | benecaster_settings_pages |
benecaster_show_settings_pages |
| Filter arguments | 1 ($pages) |
2 ($pages, $show_id) |
| Renders in | Benecaster Settings | one show’s settings |
| Slot | BenecasterSettingsPage_{id} |
BenecasterShowSettingsPage_{id} |
fillProps |
none | { showId } |
Prerequisites
- A script enqueued with
benecaster-adminas a dependency and built with Benecaster’s
React runtime mapped as externals — see Inject a React Component into a Named Slot
Declare the Tab
add_filter( 'benecaster_show_settings_pages', function ( array $pages, int $show_id ): array {
$pages[] = [
'id' => 'my-addon', // slot suffix and URL segment
'label' => __( 'My Add-on', 'my-addon' ),
'add_on' => 'my-addon', // hides the tab where the add-on is off
];
return $pages;
}, 10, 2 );
Notes
Register the filter with 10, 2. WordPress passes one argument by default. A callback that declares $show_id without the 2 raises an ArgumentCountError, and one that declares only $pages never sees the show.
Set add_on on a paid add-on. Purchase is license-wide but activation is per show, so a Multi-Show customer can own your add-on and switch it off for one show. With your entitlement slug in add_on, Benecaster hides the tab on those shows. Without it the tab appears on every show, which suits a free add-on only. The check runs on the server, so the fill does not need to repeat it.
Handle $show_id of 0. GET /admin/extensions is also fetched without a show, and the list then comes back unfiltered. Returning the page unconditionally is correct: the declaration says the page exists, and add_on says on which shows.
The tab is deep-linkable at #/shows/{id}/settings/my-addon.
Code
// my-addon/resources/js/admin.jsx — enqueued with 'benecaster-admin' as a dependency
// and built with Benecaster's React runtime mapped as externals.
import { useQuery } from '@tanstack/react-query';
const MySettingsTab = ( { showId } ) => {
const { data } = useQuery( {
queryKey: [ 'my-addon-settings', showId ],
queryFn: () => window.wp.apiFetch( { path: `/my-addon/v1/shows/${ showId }/settings` } ),
} );
return <div className="bc-space-y-4">{ /* … */ }</div>;
};
wp.domReady( () => {
window.BenecasterExtensions?.registerFill( 'BenecasterShowSettingsPage_my-addon', MySettingsTab );
} );
Hooks Used
Need this built rather than just documented? See our services →