Skip to content

Multi-Page

Pass pages instead of config to register multiple admin pages from one boot call:

Wireframe\App::boot([
'prefix' => 'my-plugin',
'option_key' => 'my_plugin',
'pages' => [
[
'page_title' => __('General', 'my-plugin'),
'config' => __DIR__ . '/config/general.php',
'menu_icon' => 'dashicons-admin-generic',
],
[
'page_title' => __('Advanced', 'my-plugin'),
'config' => __DIR__ . '/config/advanced.php',
'menu_icon' => 'dashicons-admin-tools',
],
],
]);

Each page gets its own admin menu entry, REST endpoint, and option key.

Per-page entries accept the same options as a single-page boot, with two extras (id and menu_slug):

OptionDefaultNotes
idderived from config filename or indexStable identifier used in REST routes and hook names. Set explicitly if you care.
page_titleucfirst($id)Page title and default menu label.
menu_titlepage_titleAlternate menu label.
menu_slug{prefix}-{id}Override the URL slug (?page=…).
option_key{boot_option_key}_{id_snake_cased}Storage row. Each page gets its own by default.
config—Path or inline array — same as single-page.
parentinherits from bootOverride per page. See Submenu Pages.
menu_icon'dashicons-admin-generic'Ignored when parent is set.
menu_position80 + $indexAuto-incremented if omitted.

By default each page persists into its own wp_options row ({boot_option_key}_{page_id}), so values on one page never bleed into another. Override per-page if you want a shared row:

'pages' => [
[
'id' => 'general',
'option_key' => 'my_plugin_shared',
'config' => /* … */,
],
[
'id' => 'advanced',
'option_key' => 'my_plugin_shared', // Same row → shared storage
'config' => /* … */,
],
],

Each page registers under the per-page id:

GET /{prefix}/v1/settings/{page_id}
POST /{prefix}/v1/settings/{page_id}
DELETE /{prefix}/v1/settings/{page_id}

So 'id' => 'general' becomes /my-plugin/v1/settings/general. Same pattern for table and action routes.