Contensio logo

Sidebar navigation

Place your plugin's admin links in the sidebar — Root, Tools, or Appearance, with one or many entries.

Contensio's admin sidebar has three extension placements. Your plugin declares where its links belong in plugin.json's menu block — no code required.

The three placements

Placement Where it appears Good for
root Top-level, alongside Dashboard / Pages / Posts Main user-facing plugin features
tools Inside the collapsible Tools dropdown Utilities (exports, imports, audits)
appearance Inside the collapsible Appearance dropdown Visual / theme-related settings
none No sidebar entry at all Plugin is reached via other means

Single entry

"menu": {
    "placement":  "tools",
    "label":      "Social Connect",
    "icon":       "bi-link-45deg",
    "route":      "socialconnect.settings",
    "permission": "plugins.configure"
}
  • icon — a Bootstrap Icons class (bi-*). Contensio self-hosts the full icon set.
  • route — a Laravel route name. If the route doesn't exist, the entry is hidden (safe fallback during development).
  • permission — optional. If set, users without this permission don't see the entry.

Multiple entries

A large plugin often needs more than one sidebar link. menu accepts an array of entries:

"menu": [
    { "placement": "root",       "label": "Shop",          "icon": "bi-bag",            "route": "shop.index" },
    { "placement": "root",       "label": "Orders",        "icon": "bi-receipt",        "route": "shop.orders" },
    { "placement": "tools",      "label": "Export orders", "icon": "bi-box-arrow-down", "route": "shop.export" },
    { "placement": "appearance", "label": "Storefront",    "icon": "bi-brush",          "route": "shop.storefront" }
]

A community plugin might register:

"menu": [
    { "placement": "root",       "label": "Community", "icon": "bi-people",         "route": "community.index" },
    { "placement": "tools",      "label": "Moderation","icon": "bi-flag",           "route": "community.moderation" }
]

Each entry is independent — each respects its own permission rule, its own route check, its own placement.

Active-state detection

Contensio auto-detects when the current page belongs to your plugin by matching the route name. Sidebar dropdowns (Tools, Appearance) auto-open when one of their children is active.

Route matching uses prefix — if your plugin registers shop.settings and the current route is shop.settings.save, the shop.settings sidebar entry lights up. This works well as long as you name related routes with a common prefix.

Permission gating

Set permission to a string that matches a permission you've declared in your plugin (see Permissions) or a core permission. Users without the permission don't see the entry at all — not a greyed-out link, just hidden.

Best-practice core permissions to check against:

  • plugins.configure — for configuration screens meant for admins only
  • Role-specific ones via $user->hasRole('editor') in a custom gate (rare)

What if I don't want a sidebar entry?

Set "placement": "none" (or omit menu entirely). Your plugin's pages are still reachable via their routes — they just don't show up in the sidebar. Useful if the entry point is elsewhere (e.g. a settings-hub tile via Hook::add('settings.hub_cards', ...)).

Testing

After changing plugin.json:

  1. Save the file
  2. Refresh the admin
  3. The sidebar updates live — no cache-clear needed

If the entry doesn't appear:

  • Check the permission — does your user actually have it?
  • Check the route name exists (php artisan route:list | grep shop.index)
  • Check for JSON syntax errors in plugin.json

See also