Appearance
Extension patterns
A consumer rarely extends a plugin by editing the plugin package itself. The normal pattern is to keep the plugin responsible for its shared concern, then add application-specific business logic around its supported inputs, outputs, and extension hooks.
This page is also the rule set agents should follow before changing a shared plugin. The default should be to compose around the plugin first and change the package only when the behaviour truly belongs in the shared contract.
The extension boundary
Treat a plugin as owning:
- its exported functions, classes, types, and state objects;
- its endpoint selection, merge rules, and internal caches;
- any environment switch exposed through
activeEnvironment; - any explicit extension subjects or callback parameters it exports.
Treat the consumer as owning:
- when the plugin is called;
- what context is passed in;
- how emitted state is persisted or transformed into app state;
- UI-specific filtering, presentation, analytics naming, and navigation policy;
- business rules that apply only to one consuming application.
The four safe extension patterns
1. Shape the plugin input
Add business logic before the plugin runs by controlling:
- auth context pushed into a builder plugin;
- selected profile or currency;
- template names passed to personalisation;
- current URL and custom rules passed to client-type-router.
Use this when the plugin already supports the behaviour through its public inputs.
2. Wrap the plugin output
Let the plugin produce its canonical result, then map it into application-owned state:
- persist
menuBuilder.updateAppStatein app state; - build a page-specific nav model from
getLoggedInMenuState(); - transform portfolio state before rendering;
- enrich analytics payloads before sending them.
Use this when the core plugin result is correct but your application needs a product-specific view of it.
3. Compose extra logic after the plugin
Run business rules after the plugin returns:
- filter feature-library cards for a product surface;
- decide whether to open a feature based on plugin eligibility plus app rules;
- apply route decisions after experience-manager returns a destination.
Use this when the extra rule is application-owned and should not become a shared platform rule.
4. Use explicit extension hooks
Some plugins expose supported extension points:
client-type-routeracceptscustomRulesleftNavBuilderexposesaddGenericMenuList,businessBankingLeftNavData, andixLeftNavDataportfolioexposes direct portfolio methods andUpdatePortfolioExcludes(...)feature-libraryexposes context setters and API-requirement checks
Use these first when they fit, because they keep the consumer within the published contract.
When a plugin change is required
Change the plugin package, not just the consumer, when you need to:
- add a new upstream endpoint or portfolio family;
- change shared merge logic or default routing behaviour;
- introduce a new reusable state shape or public export;
- support a new client type or new left-nav base template;
- add a new Adobe/analytics/chat integration contract;
- fix a bug in the shared logic rather than work around it in one app.
If the new rule must apply consistently across multiple consumers, it belongs in the plugin.
Agent checklist before expanding a plugin
Before changing a shared plugin, verify all of the following:
- the requirement cannot be satisfied by shaping input, wrapping output, or composing post-processing in the consumer;
- the change belongs in the shared package contract and is not app-specific behaviour;
- the public package-root API remains coherent, with any new supported export added through
src/index.ts; - mutable state, caches, reset behaviour, and environment prerequisites remain documented and understandable to consumers;
- examples and package docs still describe the supported path after the change.
If one of these checks fails, prefer extending the consumer instead of the plugin.
Public contract preservation rules
When a plugin change is justified:
- keep existing package-root imports working unless a breaking change is planned and documented;
- do not force consumers onto internal file-path imports;
- do not hide a new required step inside undocumented mutable module state;
- prefer explicit inputs, outputs, callbacks, subjects, or helper functions over implicit consumer assumptions;
- add a reset path or document why state intentionally persists for the application lifetime when introducing new module-level state.
Quick reference
| Plugin type | Extend in consumer | Requires plugin change |
|---|---|---|
| Decision plugins | pass different inputs, custom rules, or post-process result | new shared rule model or default decision path |
| Builder plugins | push context, subscribe once, transform output, use explicit hook subjects | new endpoint selection, merge logic, or exported state |
| Registry/discovery plugins | set context, run eligibility helpers, filter final list | new requirement source, cache model, or registry contract |
| Integration wrappers | enrich payloads, own UI and event wiring | new provider behaviour or transport contract |
Per-plugin rule of thumb
| Plugin | Consumer can extend by | Plugin change is needed for |
|---|---|---|
| Adobe analytics | naming events, adding context before sending, choosing when to send | new standardised event shape or shared analytics builder behaviour |
| Client type router | passing customRules, post-processing returned interactions | new default route family or shared merge rule |
| Experience manager | wrapping the returned destination with app-only policy | changing the shared pre-/post-login journey rules |
| Feature flags builder | composing returned flags with app-owned gating | new upstream endpoints or merge semantics |
| Feature library | setting context, running API checks, filtering rendered results | new requirement source or shared eligibility rule |
| Left nav builder | injecting generic/business/IX lists and wrapping built nav state | new base nav templates or duplicate-routing rules |
| Livechat | owning the UI and listening to document events | new auth/session/provider contract |
| Menu builder | subscribing to menu state and decorating the result in app state | new menu endpoint mappings or region URL rules |
| Personalisation | choosing templates and using mapping helpers before posting updates | new Target endpoint contract or shared mapping rules |
| Portfolio builder | calling direct methods, streaming results, excluding totals | new portfolio families or new shared aggregation logic |
| Profiles builder | consuming profilesList, mirroring selected profiles into app state | new profile families or new profile-source endpoints |
| Toggles builder | applying toggles to an app-specific feature list | new shared toggle mappings or fetch behaviour |
See the individual plugin pages for concrete extension snippets using the published package APIs.