Skip to content

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.updateAppState in 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-router accepts customRules
  • leftNavBuilder exposes addGenericMenuList, businessBankingLeftNavData, and ixLeftNavData
  • portfolio exposes direct portfolio methods and UpdatePortfolioExcludes(...)
  • feature-library exposes 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:

  1. the requirement cannot be satisfied by shaping input, wrapping output, or composing post-processing in the consumer;
  2. the change belongs in the shared package contract and is not app-specific behaviour;
  3. the public package-root API remains coherent, with any new supported export added through src/index.ts;
  4. mutable state, caches, reset behaviour, and environment prerequisites remain documented and understandable to consumers;
  5. 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 typeExtend in consumerRequires plugin change
Decision pluginspass different inputs, custom rules, or post-process resultnew shared rule model or default decision path
Builder pluginspush context, subscribe once, transform output, use explicit hook subjectsnew endpoint selection, merge logic, or exported state
Registry/discovery pluginsset context, run eligibility helpers, filter final listnew requirement source, cache model, or registry contract
Integration wrappersenrich payloads, own UI and event wiringnew provider behaviour or transport contract

Per-plugin rule of thumb

PluginConsumer can extend byPlugin change is needed for
Adobe analyticsnaming events, adding context before sending, choosing when to sendnew standardised event shape or shared analytics builder behaviour
Client type routerpassing customRules, post-processing returned interactionsnew default route family or shared merge rule
Experience managerwrapping the returned destination with app-only policychanging the shared pre-/post-login journey rules
Feature flags buildercomposing returned flags with app-owned gatingnew upstream endpoints or merge semantics
Feature librarysetting context, running API checks, filtering rendered resultsnew requirement source or shared eligibility rule
Left nav builderinjecting generic/business/IX lists and wrapping built nav statenew base nav templates or duplicate-routing rules
Livechatowning the UI and listening to document eventsnew auth/session/provider contract
Menu buildersubscribing to menu state and decorating the result in app statenew menu endpoint mappings or region URL rules
Personalisationchoosing templates and using mapping helpers before posting updatesnew Target endpoint contract or shared mapping rules
Portfolio buildercalling direct methods, streaming results, excluding totalsnew portfolio families or new shared aggregation logic
Profiles builderconsuming profilesList, mirroring selected profiles into app statenew profile families or new profile-source endpoints
Toggles builderapplying toggles to an app-specific feature listnew shared toggle mappings or fetch behaviour

See the individual plugin pages for concrete extension snippets using the published package APIs.