⌘K

Plugin Basics

New here? Start with Building Your First Plugin instead — this page is the reference for what you just built.

Folder layout

A plugin is a folder under /plugins/ containing at minimum a plugin.json manifest and an entry file:

plugins/my-plugin/
├── plugin.json          ← REQUIRED — manifest
├── my-plugin-init.php   ← REQUIRED — entry point, loaded when active
├── admin/
│   └── admin-page.php   ← exposes the admin page renderer (optional)
├── assets/{css,js}/
├── lang/                ← front-end translations
│   └── admin/           ← admin translations
├── data/                ← the plugin's own data store (create at runtime)
└── private/             ← secrets, rate-limit stores (create at runtime, .htaccess-protected)

Only plugin.json and the entry file it points to are required — everything else is opt-in, built by the plugin itself using the patterns in the rest of this category.

plugin.json manifest

{
    "synaptik_plugin": true,
    "name": "My Plugin",
    "slug": "my-plugin",
    "version": "1.0.0",
    "description": "One-line description shown on the Extensions page.",
    "author": "Your Name",
    "entry": "my-plugin-init.php"
}
FieldRequiredDescription
synaptik_pluginYesMust be true — distinguishes a valid plugin folder from anything else under /plugins/
slugYesStable identifier — folder name convention, activation registry key, admin route parameter. Treat as permanent once published
entryYesPath to the PHP file to require_once, relative to the plugin's folder
name, description, author, versionNoDisplay metadata on the Extensions page

The activation registry

Installing a plugin (uploading a ZIP from Admin → Extensions) does not activate it. Activation state lives in /plugins/plugins.json:

{ "my-plugin": { "active": true } }

A plugin only runs — its entry file is only require_once'd — once activated, via pl_load_active_plugins() (called once from core/functions.php on every request). Deactivating stops it from loading but preserves its data; deleting is only allowed while inactive, so an accidental click can't silently destroy stored data.

Checking if a plugin is active

if (pl_is_active('another-plugin-slug')) {
    // ...
}

Rarely needed for your own plugin (if your code is running, you're active by definition) — useful for one plugin checking whether another is present before integrating with it.