Extension API
Adminix extension points must keep the package PHP-first and runtime-generated. Use the module registry when a package or consuming app needs a custom module type while still letting Adminix resolve providers, Doctor checks, and Blade rendering through server-side configuration.
Custom modules
Register custom modules during application service provider bootstrapping, after Adminix is available in the container.
type must be a non-empty custom string and cannot reuse built-in ModuleTypeEnum values such as list, text, or resource. The module class or factory must return AdminixModuleInterface. The provider class, instance, or factory must resolve to ModuleDataProviderInterface. The optional component is a Blade component name rendered through x-dynamic-component. The optional styles and scripts arrays register frontend assets for pages that render this custom module type.
Use make:adminix_module when you want an application-owned starting point:
The generator creates a module class, data provider, app Blade component, optional Testbench test, and prints the ModuleRegistry registration snippet. It does not edit service providers automatically.
Module classes
Custom module classes may expose getType() or type() returning the same custom type string. That lets DataProviderFactory and the Blade renderer resolve the registered provider/component from an already-created module instance.
Data providers
Providers prepare module data before rendering. Keep provider output server-owned: do not trust browser-supplied datasource names, field lists, IDs, tenant keys, route params, or writable fields.
ModuleFactory, legacy ModulesFactory, object-based DataProviderFactory, and adminix:doctor all use the registry. Doctor reports MODULE_PROVIDER_MISSING when a custom module is not registered with a valid provider.
Assets
Register assets when a custom module component needs package-owned CSS or JavaScript. Pass them to register(...), or append them later with registerAssets(...) after the custom type has been registered. Adminix collects assets from the modules rendered on the current page, including nested panel modules, and deduplicates URLs while preserving registration order.
Asset URLs must be non-empty strings and cannot use the javascript: scheme. Style links are rendered after the built-in Adminix styles, so extensions can add scoped custom presentation without replacing the UI kit. Script tags are rendered with defer, after the built-in Adminix scripts in the document head.
Only server-registered assets are emitted. Do not let browser input choose asset URLs, component names, or module types.
Route hooks
Use AdminixRouteRegistry when an extension module needs package-owned endpoints under each Adminix panel path. Register hooks in a service provider register() method so they are available before Adminix boots its routes. If an extension provider may be registered before Adminix, attach the hook through $this->app->afterResolving(AdminixRouteRegistry::class, ...); the registry is resolved while Adminix routes are being loaded.
API hooks are registered inside the panel /api prefix. Web hooks are registered inside the panel prefix before Adminix's catch-all page route, so exact extension routes such as /adminix/acme-card/preview are not swallowed by page resolution. AdminixRouteContext::route() applies the panel-aware route name and _adminix_panel default. Use authMiddleware() for privileged admin traffic, or guestMiddleware() only for intentionally guest-facing admin extension routes.
For multiple panels, the same hook runs once per panel. Default-panel route names use Adminix's legacy adminix_* naming, while non-default panels use adminix.{panel_key}.*. Handlers must keep their own validation, authorization, CSRF expectations, signed context checks, and persistence rules explicit.
Testing extensions
Treat extension packages as consuming Laravel applications in tests. Use Orchestra Testbench or a workbench Laravel app that loads Adminix, registers the extension service provider, and exercises the real service provider lifecycle.
Cover at least:
ModuleRegistryregistration for every custom type, provider, component, and asset URL;Module::custom($type)andDataProviderFactoryresolution;rendered Blade output for the custom component, including embedded panel mode when supported;
AdminixRouteRegistryweb/API hooks on the default panel and at least one non-default panel when the extension supports multiple panels;route names generated through
AdminixRouteContext::routeName()and_adminix_paneldefaults applied throughroute();auth middleware, CSRF expectations, signed payloads, tenant/user scoping, validation, and not-found behavior for every custom endpoint;
browser verification for any extension surface that renders UI, loads assets, opens modals, submits forms, performs drag/drop, polls APIs, or writes data.
For frontend extensions, do not stop at rendered HTML assertions. Run a live browser against a real Testbench/workbench server, click the visible controls, and verify the resulting DOM, network response, persisted data, toast/modal state, and computed layout as applicable.
Versioning and compatibility
Adminix treats extension APIs as public package contracts once documented. Additive changes to ModuleRegistry, AdminixRouteRegistry, generated stubs, optional module methods, and context DTO helpers ship in minor releases. Bug fixes, docs, tests, generated-code polish, and backward-compatible validation improvements ship in patch releases. Breaking changes require a major release when they remove module methods, change route names or prefixes, change persisted endpoint semantics, remove generated files/options, narrow supported PHP/Laravel ranges, or require consuming applications to rewrite existing extension code.
Extension packages should pin Adminix with Composer constraints that match their tested contract, for example ^2.57, and should run their own Testbench matrix before widening support to a new Adminix minor. Do not rely on undocumented internal classes, private route names, view internals, browser-submitted metadata, or generated stub formatting. Prefer documented registries, DTOs, module interfaces, and route context helpers.
Blade components
Registered custom components receive:
module_data;page_name;params;embedded.
Component names must be application-owned or package-owned Blade component aliases. Keep frontend behavior consistent with the Adminix UI kit and verify rendered custom modules in a live browser when they have a frontend surface.