Contensio logo

plugin.json reference

Every field in the plugin manifest, with examples.

plugin.json sits at the root of your plugin. Contensio reads it on every boot to learn who your plugin is, what namespace it uses, and how it wants to appear in the admin.

Full example

{
    "name":        "acme/plugin-awesome",
    "label":       "Awesome",
    "description": "Makes things awesome.",
    "author":      "Acme Corp",
    "author_url":  "https://acme.example",
    "version":     "1.0.0",
    "provider":    "Acme\\Awesome\\AwesomeServiceProvider",
    "autoload": {
        "psr-4": {
            "Acme\\Awesome\\": "src/"
        }
    },
    "requires": {
        "cms": "^1.0",
        "php": "^8.3"
    },
    "menu": {
        "placement":  "tools",
        "label":      "Awesome",
        "icon":       "bi-magic",
        "route":      "acme-awesome.index",
        "permission": "plugins.configure"
    },
    "removable": true
}

Field reference

name (required)

The package identifier. Should match composer.json name. Recommended format: {vendor}/plugin-{slug}.

"name": "acme/plugin-awesome"

label (required)

Human-readable name shown in the admin Plugins list.

description

One-line summary shown under the label.

author / author_url

For attribution and linking in the plugin card.

version (required)

Semantic version. Increment when releasing. Must match the Git tag when publishing.

provider (required)

Fully-qualified class name of your Laravel service provider. Contensio registers this when the plugin is enabled.

autoload (required)

PSR-4 autoload map. Contensio merges this into the Laravel autoloader at runtime for ZIP-installed plugins. (Composer-installed plugins use their own composer.json autoloader, but duplicating it here keeps the dual-install flow consistent.)

requires

Compatibility gates.

  • cms — which Contensio major(s) this plugin supports. Uses Composer-style version constraints.
  • php — minimum PHP version.
  • plugins — other plugins this plugin depends on.

If the current Contensio version or PHP version doesn't satisfy these, the plugin is listed but can't be enabled.

Plugin dependencies

Declare dependencies on other plugins:

"requires": {
    "cms": "^2.0",
    "plugins": {
        "contensio/plugin-newsletter": "^1.0"
    }
}

The CMS enforces these at enable time. If a required plugin is not installed or enabled, the dependent plugin cannot be enabled and a clear error is shown. If a required plugin is disabled, the CMS warns that dependent plugins will also be disabled.

Check at runtime whether a plugin is active:

use Contensio\Support\PluginRegistry;

if (PluginRegistry::isEnabled('contensio/plugin-newsletter')) {
    // safe to use newsletter functionality
}

menu (optional)

How your plugin appears in the admin sidebar. Three placements:

  • "placement": "root" — top-level sidebar link, alongside Dashboard / Pages / Posts
  • "placement": "content" — inside the collapsible Content dropdown (alongside Posts, Pages, Media)
  • "placement": "tools" — inside the collapsible Tools dropdown
  • "placement": "appearance" — inside the collapsible Appearance dropdown
  • "placement": "none" — no sidebar entry (plugin is still accessible via its settings page or other entry points)

Fields:

"menu": {
    "placement":  "tools",
    "label":      "Awesome",
    "icon":       "bi-magic",
    "route":      "awesome.settings",
    "permission": "plugins.configure"
}
  • icon — a Bootstrap Icons class (Contensio self-hosts Bootstrap Icons).
  • route — Laravel route name to link to. If the route doesn't exist, the sidebar entry is hidden (safe fallback).
  • permission — optional. When set, only users with this permission see the entry.

Multiple entries: menu can be an array of entry objects. A big plugin (e.g. a shop) can register multiple sidebar items:

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

repository

GitHub repository slug (vendor/repo) used for automatic update checks. When present, Contensio queries the GitHub releases API daily and shows an "Update available" badge in the Plugins panel when a newer release exists.

"repository": "acme/plugin-awesome"

The download URL is assembled as:

https://github.com/{repository}/archive/refs/tags/{tag}.zip

Only public repositories are supported without authentication.

update_url

Custom HTTPS endpoint for update checks — for private or self-hosted plugins where GitHub is not the distribution channel. Ignored when repository is also set.

"update_url": "https://store.acme.example/api/plugin-updates/awesome"

Contensio will POST to this URL with:

{ "current_version": "1.0.0" }

The endpoint must respond with:

{
    "version": "1.1.0",
    "download_url": "https://store.acme.example/download/awesome-1.1.0.zip",
    "changelog_url": "https://store.acme.example/changelog/awesome"
}

Both version and download_url are required. changelog_url is optional and shown as a "What's new" link in the admin. The download_url must also use https://.

For paid plugins that require a license key, see the contensio/plugin-update-info filter — it gives the plugin full control over how update info is fetched and the download URL is generated.

removable

true (default) — plugin can be disabled + uninstalled via the admin. false — plugin is load-bearing and locked (rare; for bundled plugins shipped with the core distribution).