Skip to content

Packages

Capell packages extend the CMS without adding feature code to Core, Admin, or Frontend. Use a package when a capability can be installed, versioned, tested, and disabled independently.

Use this section if you build or maintain a Capell package.

I need to…Read
Get from a scaffold to a rendering packageExtension and theme development
Decide between host, package, and app codeHost, package, or app code
Build a package from start to finishBuild an extension end to end
Find the shortest path for a package taskPackage authoring jobs
Understand package files and structurePackage anatomy
Choose an extension pointExtension point chooser
Add admin surfacesAdmin extensions
Add anonymous-safe frontend outputFrontend extensions
Look up exact contracts, tags, and testsExtension point API reference
Browse every extension surface in one listExtension surface catalogue
Know which Capell versions a package supportsExtension API versioning
Test a packageTesting packages
Debug missing package outputExtension troubleshooting
Plan the next Core compatibility lineCore Filament neutrality and 2.x preparation

Use Extension surface vocabulary when you need the shared definitions for packages, surfaces, contributions, capabilities, install impact, and Marketplace proof.

Package is right when…Keep it in the app when…
The feature has its own settings, routes, resources, widgets, jobs, or frontend output.It is one-off project glue with no reuse value.
It should be installable through Composer or Marketplace.It depends heavily on private app models.
It needs its own tests and release cadence.The behavior is just app configuration.
It contributes through Capell extension points.It needs to patch host package classes.

If you are unsure, use Host, package, or app code.

Every package should have:

  • composer.json
  • capell.json
  • at least one service provider
  • README.md
  • translations for visible labels
  • focused package tests

Packages with admin UI usually also include translations, Filament resources/pages/widgets, settings schemas, an admin bridge, and feature tests.

Use capell:make-extension for new local package scaffolds. The command keeps the existing extension command name, but the generated files use package language and manifest v3 provider buckets.

Interactive mode asks for missing values:

Terminal window
php artisan capell:make-extension

Non-interactive scripts must pass the package name, profile, and target directory:

Terminal window
php artisan capell:make-extension vendor/example --profile=minimal --path=packages --name="Example"
php artisan capell:make-extension vendor/example-tools --profile=full --path=packages --premium

Use minimal for a lean installable package: Composer metadata, capell.json, one runtime provider, translations, README, a phpunit.xml.dist, a Testbench TestCase, and a manifest test. Use full when you want live examples of the metadata/install/admin provider buckets, a package-owned command, a settings class, a Layout Builder content widget with typed Data objects, and package-owned widget assets; it adds a provider-discovery test.

Neither profile scaffolds public-output safety tests, render hooks, or Actions — write those yourself. The generated TestCase also registers only your own provider, so add CapellServiceProvider before testing anything that touches Capell; see Standalone package harness.

Capell packages are Composer packages. For a published package:

Terminal window
composer require vendor/example
php artisan capell:package-cache:clear

For local development, add a Composer path repository to the app for the package path you are editing:

Terminal window
composer config repositories.vendor-example path packages/example
composer require vendor/example:@dev

Use a wildcard path repository in composer.json when the app should load several local packages:

{
"repositories": [
{
"type": "path",
"url": "packages/*",
"options": {
"symlink": true
}
}
]
}

For a private Git repository, configure a Composer VCS repository:

Terminal window
composer config repositories.vendor-example vcs [email protected]:vendor/example.git
composer require vendor/example:^1.0

Use symlink: true path repositories during local development so package edits apply without reinstalling. Switch to a tagged constraint such as ^1.0 once the package is published. See Package authoring for scaffold profiles and Marketplace sharing.

Keep the manifest boring and explicit. It should identify the package, provider classes, version constraints, contribution metadata, settings ownership, lifecycle commands, and Marketplace metadata when relevant.

Use package-owned manifests and generated extension pages as the source of truth for public add-on package names. Use Packages and extensions for host package boundaries and authoring entry points.

Package providers should register only what the package owns:

  • config and translations
  • migrations and settings migrations
  • package settings classes
  • admin bridges and admin surface contributions
  • render hooks, assets, frontend widgets, and cache invalidation rules
  • routes, commands, policies, events, and jobs

Do not register optional-package behavior from the host repo. The package that owns the feature should register it.

Use the extension point chooser before adding a new registration path. Use the extension point API reference when you need exact contracts, tags, and test recipes. Use extension troubleshooting when a package is installed but its admin, frontend, settings, cache, or marketplace contribution does not appear.

Installer and Marketplace surfaces have their own narrow contracts:

  • Put domain behavior in src/Actions/*Action.php.
  • Put structured inputs/outputs in src/Data/*Data.php.
  • Cast JSON settings/state to Data objects where practical.
  • Keep Filament resources, Livewire components, controllers, commands, and jobs thin.
  • Use translations for visible strings.

Command, resource, or job tests should fake Actions when the Action already has direct coverage.

Deciding between an AdminBridge and a direct contribution:

  • Contributing several admin concerns (resources + widgets + settings)? Use an AdminBridge.
  • Just one surface? A direct CapellAdmin::contributeToAdminSurface(...) call is fine.
  • Unsure? Prefer a bridge. One class is easier to audit than scattered calls.

Use an AdminBridge when a package contributes more than one admin concern:

use Capell\Admin\Data\Bridges\AdminBridgeContextData;
use Capell\Admin\Support\Bridges\AbstractAdminBridge;
use Capell\Admin\Support\Bridges\AdminBridgeRegistrar;
final class PackageAdminBridge extends AbstractAdminBridge
{
public function register(AdminBridgeRegistrar $registrar, AdminBridgeContextData $context): void
{
$registrar->resource(ExampleResource::class, group: 'content', name: 'examples');
$registrar->widget(ExampleWidget::class);
$registrar->settingsClass('examples', ExampleSettings::class);
$registrar->settingsSchema('examples', ExampleSettingsSchema::class);
}
}

Extend AbstractAdminBridge. The AdminBridge interface also declares isEnabled(AdminBridgeContextData $context): bool, so implementing the interface directly without it is a fatal error; the abstract class provides a default that returns true.

Small packages can contribute directly with CapellAdmin::contributeToAdminSurface(...), but bridges are easier to audit.

Useful admin surfaces:

NeedUse
Filament page/resource/widget/configuratorAdminSurfaceContributionData through AdminBridgeRegistrar.
Page, site, layout, or user form fieldsTagged schema extenders.
Dashboard Filament widgetsCapellAdmin::registerDashboardFilamentWidget(...).
Header toolsAdminToolItem::TAG.
User menu itemsCapellAdmin::registerUserMenuItem(...).
Admin widgetsCapellAdmin::registerDashboardFilamentWidget(...).

Frontend package code must preserve public HTML safety. Anonymous output must not expose authoring selectors, model IDs, field paths, signed admin URLs, permissions, or package internals.

NeedUse
Small HTML injectionRenderHookRegistry::register(...).
Public widgetLayoutWidgetRegistry::register(...) with LayoutWidgetTarget::FrontendBlade or FrontendLivewire, when the Layout Builder package is installed.
Package CSS/JSTailwindAssetsRegistry::registerSource(...) and registerImport(...).
Page cache invalidationCacheInvalidationRegistry::registerDependency(...).
Static-site export hookStaticSiteExtensionRegistry::register(...) when the static export package is installed.
  • Package migrations live in the package.
  • Settings migrations live in database/settings/.
  • Guard settings migrations with table/column existence checks.
  • Writes go through Actions, not model methods.
  • If the package introduces public rendering data, make sure controllers/actions load it before Blade renders.

Cover package behavior where it lives:

  • Action tests for domain behavior.
  • Feature tests for routes, commands, Filament pages/resources, and package install/setup.
  • Public rendering tests for frontend output and safety.
  • Architecture tests for package boundaries.
  • Manifest/provider tests so package discovery fails loudly.

When testing commands or UI surfaces that only orchestrate Actions, fake the Action boundary with Laravel Actions fakes instead of running the Action again. The surface test should prove prompts, options, output, permissions, and Action invocation; the Action test should prove domain behavior.

Use the narrowest package Pest command during development, then broaden before release:

Terminal window
vendor/bin/pest packages/admin/tests --configuration=phpunit.xml

Use the package checklist before release.

  • composer.json and capell.json match the package name.
  • Provider registers only package-owned behavior.
  • Visible strings use translations.
  • Actions and Data objects cover meaningful boundaries.
  • Migrations are idempotent for installed apps.
  • Public frontend output is safe for anonymous users.
  • Package tests pass in isolation.
  • README explains install, config, commands, extension points, and troubleshooting.
NeedRead
Add ready-made page sectionsContent Sections
Build pages visually with widgetsLayout Builder
Understand package/surface/capability termsExtension surface vocabulary
Write package service providersService providers
Understand the extension lifecycleExtension lifecycle
Use Actions, Data, and settings correctlyActions, Data, and settings
Ship migrations and settings tablesDatabase and migrations
Study worked extension examplesExtension examples
Group packages into productsPackage product groups
Scaffold packages with AI assistanceAI creator
Understand package authoring as a platformPackage authoring
Create an installable theme packageCreating custom themes
Understand package boot/provider bucketsPackage boot lifecycle
Debug Composer/manifest/provider discoveryDebugging package discovery
Avoid unsafe package patternsDo not do this