<!-- Documentation snapshot: package 5.0.0-beta.1; channel next; base revision b06750c507494441f0b2298766b70087e45346a2; local changes false; original source SHA-256 a835cd45ae3fe1bf0a35d6684d74bb9ac2806a91907e57a7cb4e344734e8aba1. -->

# Marionette v4 to v5 Compatibility Ledger

This ledger records the public compatibility boundary between Marionette v4
and v5. It is a reference, not the full procedural upgrade guide. Detailed
upgrade steps that are not already documented are tracked in
[the stable-release documentation issue](https://github.com/marionettejs/marionette/issues/147).

Status values describe the v5 outcome:

- **Preserved**: the supported public behavior remains available.
- **Changed**: the public behavior remains relevant but has a different contract.
- **Removed**: the v4 behavior is not supported in v5.
- **Added**: v5 provides a new public capability with no core v4 equivalent.
- **Optional**: the behavior is available only through an explicit opt-in.
- **Renamed**: the capability remains under a different name.
- **Documented**: the public extension point is retained and called out here.

## Compatibility ledger

| Area | v4 behavior | v5 behavior | Status | Migration note |
| --- | --- | --- | --- | --- |
| Package name | Installed as `backbone.marionette`. | Published as `marionette`. | Changed | Replace the package name. See [Installing Marionette](/docs/installation.md#install). |
| Install command | `npm install backbone.marionette` installed Marionette under the v4 name. | Install core with `npm install marionette@5.0.0-beta.1`; add optional peers only when used. | Changed | See [peer dependencies](/docs/installation.md#peer-dependencies). |
| Default export namespace | Namespace-style default import usage was supported. | A default namespace export is not supported. | Removed | Use named imports. See [Quick start](/docs/installation.md#quick-start); final migration guidance is tracked in [#147](https://github.com/marionettejs/marionette/issues/147). |
| Named exports | Classes and utilities were available as named exports. | Named exports are the supported module API. | Preserved | Import only what is needed, for example `import { View, Region } from 'marionette';`. |
| Feature flags | `setEnabled` and `isEnabled` configured one module-global registry, including `childViewEventPrefix`, `triggersPreventDefault`, `triggersStopPropagation`, `DEV_MODE`, and application-owned names. | The registry and both named exports are removed. Child event prefixes remain configurable per View. Trigger default prevention and propagation remain configurable per trigger. | Removed | Remove flag calls and imports. The disabled `triggersPreventDefault` and `triggersStopPropagation` flags globally inverted trigger defaults and have no global replacement; set `preventDefault: false` or `stopPropagation: false` on each affected trigger. Set `childViewEventPrefix` on the owning View, and move application-owned values to Application State or explicit application configuration. Future deprecations use cataloged diagnostics instead of `DEV_MODE`. |
| Backbone dependency | Backbone was a required runtime dependency and supplied core model, collection, event, and view behavior. | Marionette core does not import Backbone. Plain objects and arrays use the neutral DataApi; Backbone is an explicit integration. | Optional | Install `@mnjs/adapters` and configure its `BackboneApi` with `setDataApi()` for model/collection use and `setStateApi()` separately for Backbone state observation. See [Optional Backbone](/docs/backbone.md). |
| Model and collection data | Core read Backbone-specific `cid`, `attributes`, `get`, `models`, `indexOf`, and structural event payloads directly. | Core reads identity, values, serialization, ordered model snapshots, subscriptions, and structural changes through DataApi. | Changed | Use plain objects and arrays with the default adapter, configure `BackboneApi` for Backbone, or configure a custom adapter with `setDataApi`. See [Data API](/docs/data-api.md). |
| Serialized collection template property | A View with a collection and no model supplied the result of `serializeCollection()` to the template as `items`. | The template receives that result as `models`, matching the DataApi vocabulary while remaining distinct from the raw ordered snapshot returned by `DataApi.models(collection)`. Default serialization returns an array of serialized values; an override may return another shape. The pre-stable `items` property is removed. | Changed | Replace collection-template reads and destructuring of `items` with `models`; do not retain a fallback for both names. |
| Region display input | `Region#show` and `View#showChildView` accepted a View instance, template function, string, or View-options object. Non-View values implicitly constructed a base Marionette View. | Both methods require a Marionette View instance. The public types require an instance; Regions do not allocate hidden Views or provide a custom diagnostic for unsupported input shapes. | Changed | Construct the intended View explicitly and pass the instance. Replace strings or template functions with `new View({ template: () => content })`, and wrap former View-options objects with `new View(options)`. |
| Local state | Core did not provide a first-class state-source composition contract; applications commonly used a Backbone model or separate mixin. Toolkit mixed Backbone-backed `getState(attr)`, `setState`, `toggleState`, `hasState`, and reset helpers into several owners. | `Application`, `MnObject`, `View`, `CollectionView`, and `Behavior` compose an exact source through borrowed `state` or owned `createState(options)`. `getState()` returns that source, and StateApi observes `stateEvents`. The v5 alpha concrete `State` export is removed. | Changed | For simple local values, return a plain object from [`createState()`](/docs/state.md) and use property access. For reactive state, supply the provider's source and configure its StateApi. Move mutation to the source's native API; core adds no universal mutation wrappers. |
| Explicit Backbone integration | Backbone integration was applied as part of the v4 dependency relationship. | `@mnjs/adapters/backbone` provides one combined DataApi and StateApi adapter without modifying Backbone objects, prototypes, or native event behavior. | Optional | Pass `BackboneApi` to the selected runtime's `setDataApi()` and `setStateApi()` methods before constructing Marionette owners that consume those sources. See [Optional Backbone](/docs/backbone.md). |
| jQuery dependency | jQuery commonly backed Backbone view and Marionette DOM behavior. | Marionette core does not import jQuery. | Optional | Install jQuery only when using `@mnjs/adapters/dom/jquery`. See [jQuery DOM adapter is optional](/docs/installation.md#jquery-dom-adapter-is-optional). |
| Optional jQuery DomApi | jQuery-backed DOM operations were part of the common v4 stack. | The `@mnjs/adapters/dom/jquery` subpath provides explicitly selected jQuery-backed DOM methods without adding `$el` to core. | Optional | Configure it at app boot with `setDomApi`. See [jQuery DOM compatibility](/docs/upgrade-guide.md#jquery-dom-compatibility). |
| `$el` | Views and Behaviors exposed a jQuery wrapper. | Core and adapters do not create `$el`; Views keep a fixed root. | Changed | Assign `this.$el = $(this.el)` in the View, CollectionView, or Behavior `initialize()` when needed. See [jQuery DOM compatibility](/docs/upgrade-guide.md#jquery-dom-compatibility). |
| `view.$(selector)` | Returned a jQuery collection in the common Backbone/jQuery configuration. | Delegates to `DomApi.findEl`, which returns a native `NodeList` by default or a jQuery collection with the optional adapter. | Changed | Prefer native collection APIs, or opt into `@mnjs/adapters/dom/jquery`. See [jQuery DOM compatibility](/docs/upgrade-guide.md#jquery-dom-compatibility). |
| CollectionView `attachHtml` container | The second override argument was a jQuery-wrapped `$container`. | The second argument is the native child container element, equal to the CollectionView's `el` unless `childViewContainer` selects another element. | Changed | Update overrides to accept `attachHtml(els, container)` and pass the native `container` to DomApi operations. Do not depend on jQuery collection methods or restore a dual-shape argument. |
| Region element resolution | Construction resolved selector strings through public `Region#getEl` before `initialize`; `getEl` returned a jQuery collection, and custom DomApi adapters could implement `getEl(selector)`. | Construction preserves the configured selector for `initialize` and defers public `Region#getEl` dispatch until the first DOM operation. `Region#getEl` returns the first matching native DOM element. `DomApi#getEl` is removed; selector lookup delegates to `findEl(context, selector)`. | Changed | Do not rely on constructor-time DOM lookup or `getEl` side effects. Make Region `getEl` overrides return one native DOM element. Replace DomApi `getEl` overrides with `findEl`, returning an array-like collection whose first entry is the matched element. |
| Region `el` input | Accepted a selector string, DOM element, or jQuery-wrapped element. | Selector-string and DOM-element support are preserved. A jQuery collection is rejected even when the optional DomApi adapter is selected. | Changed | Pass the native element, such as `wrappedElement[0]`. Region remains the Marionette mount-point abstraction. See [View `el` is element-only](/docs/upgrade-guide.md#view-el-is-element-only). |
| View `el` | Selector strings and jQuery-wrapped elements commonly worked through Backbone and jQuery. | `View` accepts a DOM element only and throws a migration hint for strings or wrappers. | Changed | Resolve a selector with `document.querySelector(...)` or unwrap a jQuery collection with `[0]`. See [View `el` is element-only](/docs/upgrade-guide.md#view-el-is-element-only). |
| View root replacement | `setElement()` transferred a View to another element. | View and CollectionView roots are fixed at construction; `setElement()` is removed and the public instance `el` is readonly. | Removed | Supply `el` at construction. For a different root, destroy the old View and construct a new owner. See [View roots are fixed at construction](/docs/upgrade-guide.md#view-roots-are-fixed-at-construction). |
| View DOM attributes | View attribute cloning could copy inherited enumerable properties. Attributes were applied through jQuery. | Attribute maps contribute own enumerable string keys and use DOM attribute names. `renderAttributes()` refreshes root attributes without rendering content. Only explicit `null` removes an attribute; undefined and omitted entries leave it untouched. Other values use native string conversion. | Changed | Use own attribute declarations and `null` for removal. For boolean HTML attributes use `disabled: isDisabled ? '' : null`; `false` becomes the string `"false"`. Set live form properties explicitly. Keep using the View-level `className` option; inside `attributes`, use `class` and `for`. |
| CollectionView `emptyView` | Direct falsy values and resolvers returning `undefined`, `null`, or `false` disabled the empty view; other invalid definitions were skipped silently or failed with incidental errors. | Omitted values, direct `undefined`, `null`, or `false`, and resolvers returning those values disable the empty view. The public types describe these alternatives; no custom shape diagnostic is emitted. | Preserved | Existing conditional resolvers require no migration; return a View class or a supported disabled value. |
| Detach semantics | jQuery detach operations preserved jQuery listener and data bookkeeping for detached nodes. | The native DomApi removes nodes without cleaning their listeners or jQuery data; the optional adapter delegates to jQuery detach operations. Referenced nodes retain their handlers and data in either case. | Changed | Choose the optional jQuery adapter for its documented query and content-operation semantics, not merely to retain referenced nodes during detach. See [`detachContents` policy](/docs/upgrade-guide.md#detachcontents-policy). |
| Radio singleton | Applications commonly consumed the separate `backbone.radio` package, which also backed Marionette's `channelName`, `radioEvents`, and `radioRequests` integration. | Marionette exports its own built-in `Radio` singleton. It does not share channels with `backbone.radio`. | Changed | Replace every `backbone.radio` import together with the Marionette upgrade, including publishers and requesters outside Marionette classes. A mixed migration silently creates two disconnected buses. Do not bridge or mirror them. See [Atomic Radio migration](/docs/upgrade-guide.md#atomic-radio-migration). |
| Multiple Marionette configurations | Root imports and mutable class configuration were effectively process-scoped. | Root imports still form one default runtime. Optional `createMarionette()` calls create isolated runtimes with their own runtime classes, adapters, renderer configuration, and Radio registries. | Added | Keep ordinary root imports unless isolation is required. When using `createMarionette()`, construct Regions and child Applications from the selected runtime. See [Runtime isolation](/docs/runtime-isolation.md). |
| Radio debug configuration | `Radio.DEBUG = true` enabled Backbone.Radio diagnostics. | Use `Radio.setDebug()` and `Radio.setDebug(false)`. The `DEBUG` property is not supported. | Changed | Replace assignments with the explicit method during the atomic Radio migration. |
| Radio request mixin | Backbone.Radio exposed `Radio.Requests` for direct mixin use. | `Requests` is a named export from `@mnjs/radio`; request/reply methods remain on channels and the top-level Radio API. | Changed | Import `Requests` from the Radio package and compose it with `Object.assign({}, Requests)`. |
| Radio diagnostic override hooks | Backbone.Radio exposed `Radio.log` and `Radio.debugLog`. | Both hooks are replaceable on each Radio instance; `setDebug` gates custom warning hooks. | Changed | Assign hooks on the Radio instance your channels use. Hooks receive that Radio as `this` and replacements apply to existing channels. |
| Radio Channel construction and registry | Backbone.Radio exposed its Channel constructor and registry. | `Channel` and `Requests` are named exports from `@mnjs/radio`; each Radio also exposes its Channel constructor. The registry remains private. | Changed | Use `Radio.channel(name)` for shared channels or `new Channel(name)` for independent channels. Standalone owners call `reset()` themselves. |
| Radio method receiver | Backbone.Radio top-level methods read their channel factory and registry from `this`, so borrowed methods could target an alternate receiver. | Top-level methods dispatch through the Radio instance that created them. | Changed | Use `createRadio()` for another registry. Borrowing a method does not create or select another Radio instance. |
| Radio named reset | Resetting an unknown channel could fail with an incidental `TypeError`, and names matching inherited object properties could resolve incorrectly. | `Radio.reset(name)` throws `MN0021` for an unknown channel, while explicitly created channel names are registry-owned. | Changed | Create the channel before resetting it, or call `Radio.reset()` with no arguments to reset all existing channels. |
| Request-name ownership | Inherited request-registry properties could be mistaken for named handlers or the `default` fallback. A `__proto__` request name could change the registry or result-map prototype instead of becoming an own entry. Flattening a nested multi-request result could copy inherited enumerable properties. | Only explicitly registered own handlers are invoked or reported as overwritten. Request result maps use safe own string properties, including for `__proto__`, and nested results contribute own enumerable string properties only. | Changed | Register every named and default handler explicitly; do not depend on request-registry prototype inheritance, and move intended nested result values onto the result object itself. |
| UMD global | Script builds exposed the `Marionette` global. | Unminified and minified UMD compatibility builds remain supported throughout v5 for no-bundler, AMD, and `Marionette`-global consumers. | Preserved | Existing direct-script integrations can retain the global while updating changed APIs. New applications should use the canonical ESM entry. |
| CJS entry | CommonJS consumers could require Marionette. | `require('marionette')` resolves to the CJS compatibility build and returns named API properties throughout v5. | Preserved | Legacy Node and build-tool consumers can destructure the required API instead of expecting a restored default namespace contract. New applications should use ESM. |
| ESM entry | ES module named imports were supported. | `import` resolves to the ESM build and named imports remain supported. ESM is the canonical distribution for new applications. | Preserved | Use named imports from `marionette`. |
| Underscore dependency | Marionette required Underscore through its package dependency relationship. | Marionette core does not import or declare Underscore as a peer dependency. | Removed | Remove Underscore if it was installed only for Marionette; keep it when application code uses it directly. Backbone manages its own dependency. See [Underscore is no longer a peer dependency](/docs/upgrade-guide.md#underscore-is-no-longer-a-peer-dependency). |
| Client ID sequence ownership | Marionette constructors and event-listener bookkeeping drew IDs from Underscore's counter, so external Underscore calls could affect later Marionette numeric suffixes. | One loaded copy of Marionette owns one sequence shared by its constructors and event-listener bookkeeping. Complete Marionette-generated IDs remain unique when types reuse a custom prefix, but the sequence is not coordinated with Underscore or Backbone. | Changed | Treat `cid` values as opaque stable instance identifiers. Do not parse or compare numeric suffixes, depend on allocation order, or coordinate IDs through `_.uniqueId`. |
| Class extension input inheritance | Inherited enumerable properties on the `staticProps` hash passed to `extend` could become child-constructor properties. | The public `protoProps` and `staticProps` hashes contribute own enumerable string and symbol keys. Inherited enumerable statics from the parent constructor remain available on the child. | Changed | Move intended prototype and static definitions onto the corresponding input hash itself; do not inherit configuration into either hash. |
| Instance option and render-data inheritance | Underscore-backed shallow merges could copy inherited enumerable properties when combining constructor options and resolved defaults, Region definitions and defaults, `childViewOptions`, or serialized data and `templateContext`. `mergeOptions` could also read inherited or non-enumerable named properties. | Constructor/default options, Region options, and child View options use own enumerable string and symbol properties and safely preserve a literal own `__proto__` property. `mergeOptions` copies only requested own enumerable string properties. Serialized data and `templateContext` use own enumerable string and symbol properties when both are combined; a one-sided fast path still returns the original object unchanged. | Changed | Move intended merged values onto the supplied object itself; do not use prototype inheritance or non-enumerable properties for these inputs. |
| Target-first root utilities | The package root exported `bindEvents`, `unbindEvents`, `bindRequests`, `unbindRequests`, `mergeOptions`, `getOption`, `normalizeMethods`, and `triggerMethod` wrappers that accepted any compatible target object as their first argument. | These conventions have one canonical form as methods on Marionette instances. The root exports, their internal proxy helper, and the public adapter for applying them to arbitrary plain objects are removed. | Removed | Call the corresponding instance method, such as `owner.normalizeMethods(bindings)` or `owner.bindEvents(entity, bindings)`. When a plain object needs similar behavior, extend `MnObject` or own that local adapter explicitly rather than borrowing a Marionette prototype method. |
| `mergeOptions` key collection | Underscore's iterator accepted strings, `arguments`, generic array-like objects, and ordinary object values as requested option names. Invalid or missing key collections were silently ignored. | `mergeOptions(options, keys)` requires `keys` to be an Array when options are present; the declared contract replaces custom shape validation. | Changed | Pass the requested option names as an Array. |
| Private immediate-child traversal | The private `_getImmediateChildren()` result was passed to Underscore's generic iterator, which also traversed arbitrary keyed objects. | Marionette-owned implementations return Arrays. The private type requires an Array; traversal trusts that contract. | Changed | Do not override private `_getImmediateChildren()`. Use documented View, Region, and CollectionView APIs to own child Views. |
| View constructor option precedence | `View` and `CollectionView` passed options to `preinitialize`, then Backbone assigned the public `model`, `collection`, `el`, `id`, `attributes`, `className`, `tagName`, and `events` options before creating the element. Supplied options therefore won conflicts with assignments made by the hook. | The hook can observe the supplied options before the same public constructor options are reapplied. Conflicting supplied options remain authoritative, while v5's private initialization order is unchanged. | Preserved | Use `preinitialize` to derive early state from constructor options. Use `initialize` when an intentional replacement must occur after final public option assignment and element setup. |
| View `preinitialize` and internal setup order | Before Backbone invoked the host's `preinitialize` hook, `View` installed lifecycle monitoring and constructed Behaviors and Regions; `CollectionView` installed monitoring and constructed its child storage and Behaviors. | The standalone host constructor invokes `preinitialize` before that internal setup. Behavior `initialize` can therefore read host state established by the hook, while the hook cannot depend on constructed Behaviors, Regions, child storage, or lifecycle monitoring. | Changed | Keep early host state derivation in `preinitialize`. Move code that needs Marionette-owned collaborators or lifecycle dispatch to the host's `initialize` method. |
| CollectionView empty Region initialization | The constructor invoked the host's `initialize` before calling overridable `getEmptyRegion()` to establish the default empty-view Region. | The same order is preserved. An override can depend on state established by `initialize`; calling `getEmptyRegion()` from `initialize` remains safe because the later constructor call reuses that Region. | Preserved | Keep empty-Region override setup in `initialize` or `getEmptyRegion`; no migration is required. |
| Destruction during View initialization | A `View` or `CollectionView` destroyed from its own `initialize` continued the constructor tail, rebinding entity events and firing Behavior `initialize` after Behavior destruction. `CollectionView` also replaced its destroyed empty Region with a live one. | Destruction is terminal. After `initialize` returns, a destroying or destroyed host skips remaining constructor setup; a destroyed CollectionView retains its destroyed empty Region. | Changed | Initialization code may destroy a host without adding guards for later constructor setup. Do not expect entity events, Behavior initialization hooks, or a live empty Region after that destruction. |
| Child View collection helpers | `CollectionView#children` proxied Underscore collection methods, including iteratee shorthand, a private-array callback argument, Underscore return values, deep/function-form `invoke`, array-form deep paths in `pluck`, and count coercion. | The 19 documented helpers are owned by Marionette. Callback methods require functions and expose only View and index; `each` returns the child container; `reduce` follows native initial-value rules; `invoke` accepts a direct string method; `pluck` reads one direct View property; positional counts are nonnegative integers. Callback and method shapes are checked by the public types. Invalid counts and an empty reduction without an initial value still throw `MN0024`. | Changed | Replace `map('id')` with `map(view => view.id)` or `pluck('id')`. Replace `pluck(['model', 'cid'])` with `map(view => view.model?.cid)`. Do not use the callback's former third argument or depend on `each` returning an array. Replace deep/function-form `invoke` with an explicit callback, and pass valid integer counts. |
| Child View collection aliases and iteration | Undocumented Underscore aliases such as `forEach`, `detect`, `select`, `all`, `any`, and `include` were available through the proxy. The child container was not natively iterable. | The aliases are removed. The canonical methods remain, and the child container supports `for...of`, spread, destructuring, and `Array.from`. | Changed | Replace the aliases with `each`, `find`, `filter`, `every`, `some`, and `contains`, respectively. Prefer native iteration when no collection-helper return value is needed. |
| Child View identity and ownership | Plain-object cid indexes could mistake inherited property names for children, and `removeChildView` or `detachChildView` could mutate a supplied View that was not actually owned by the CollectionView. | View cids remain Marionette-owned keys. Model identity comes from `DataApi.key()` and is stored in a `Map`; `findByModelCid` is removed while `findByModel` uses the configured adapter. Membership requires the exact View instance. | Changed | Replace `findByModelCid(cid)` with `findByModel(model)`. Configure stable source identity through DataApi rather than adding `cid` to otherwise neutral application data. |
| CollectionView filter iteration | Underscore supplied CollectionView child iteration and predicate-object matching, including accepting arrays as predicate objects. | Function filters run with the CollectionView as their receiver and receive `(childView, index, liveChildArray)` while traversing the initial length densely. Predicate maps snapshot own enumerable string keys and values per pass, require present strictly equal model attributes, and exclude inherited, symbol, and non-enumerable keys. Arrays are not predicate maps. | Changed | Use a function filter for custom matching or mutation-sensitive logic. Supply predicate shorthand as an ordinary object and use the same object reference for nested attribute values that should match. |
| CollectionView removal-only updates | Every collection update flowed through sort, filter, and child rendering, so removing one model could move every surviving child through a document fragment even when default collection ordering or disabled ordering already preserved their relative order. | A removal-only update on an already-rendered, unfiltered CollectionView with default collection ordering or ordering disabled destroys the removed child without moving or rerendering synchronized visible survivors when the default sort, filter, and comparator-query methods are used. Empty-view, deferred-child, custom filter or comparator, overridden query methods, add, and merge cases retain the full update path. | Changed | Observe `remove:child` for removals. Do not rely on sort or `render:children` firing when no surviving child needs sorting, rendering, or placement. Private `_viewComparator` and `_onCollectionUpdate` overrides are not supported extension points; use the documented public options and methods. |
| Event bookkeeping rename | Backbone-compatible private event fields such as `_events`, `_listeningTo`, and `_listenId` could be observed. | Marionette's built-in Events implementation uses private `_rdEvents`, `_rdListeningTo`, `_rdListeners`, and `_rdListenId` fields. | Renamed | Do not read or write event bookkeeping fields; use `on`, `off`, `listenTo`, and `stopListening`. Procedural guidance is tracked in [#147](https://github.com/marionettejs/marionette/issues/147). |
| Event aliases | Backbone.Events exposed `bind` as an alias of `on` and `unbind` as an alias of `off`, including on Radio channels. | Marionette Events and built-in Radio expose only the canonical `on` and `off` methods. The optional Backbone integration does not modify Backbone objects, so native Backbone instances retain `bind` and `unbind`. | Changed | Replace the aliases on Marionette objects and Radio channels. Existing Backbone-owned code may continue using Backbone's native aliases, though `on` and `off` remain preferred. |
| Event and request override dispatch | `once` registered through overridable `on`; `listenToOnce` registered through overridable `listenTo`; and Backbone.Radio registered `replyOnce` through overridable `reply`. Map and space-separated reply operations dispatched each entry through their public method. | The same public override dispatch is preserved. Emitter interoperability calls the documented three-argument `on` and `off` methods once per binding, including when an override delegates with only those documented arguments. | Preserved | Lifecycle and instrumentation mixins may continue overriding the canonical registration methods. Overrides should delegate synchronously when they want the base registration behavior. |
| Object-form event triggering | Backbone.Events treated map keys as event names, ignored the mapped values, and passed arguments after the map to each handler. | Object-form `trigger` is a Marionette extension: each map value is the sole argument for its event, and arguments after the map are not forwarded. | Changed | Move the intended per-event handler argument into each map value. Use separate string-form `trigger` calls when an event needs multiple arguments. |
| Object-form requests | Backbone.Radio passed each mapped value as the first argument to its handler and forwarded arguments after the map. | Marionette preserves that argument order and returns the same per-name result map shape. | Preserved | No request-call change is required. |
| Event-name ownership | Event names matching inherited object properties could fail during registration or consult inherited private-store entries. A literal `__proto__` name could affect the event store's prototype. | Only explicitly registered own event names are dispatched or removed. `constructor`, `toString`, `__proto__`, and other inherited names are ordinary event names, and `__proto__` does not change the store prototype. | Changed | Register and remove event names through `on`, `off`, `listenTo`, and `stopListening`; do not modify or inherit from private event stores. |
| Entity-event map `__proto__` name | Declarative entity-event maps silently discarded an own enumerable `__proto__` name during normalization, so it was never bound or selectively unbound. | `bindEvents` and selective `unbindEvents`, including `modelEvents`, `collectionEvents`, and `radioEvents`, throw `MN0026` before delegation when the map has an own enumerable `__proto__` entry. Marionette does not reject other prototype-collision names: its Events API supports them, but third-party emitters such as Backbone may not safely support every name. | Changed | Rename the `__proto__` entity event or bind it through an entity API that explicitly supports the name; verify other prototype-collision names against the entity's emitter, and omit the map to unbind every event from an entity. |
| `Mn.Object` | The default namespace exposed an `Object` alias for the Marionette object class. | The alias is not restored. | Removed | Use `import { MnObject } from 'marionette';`; final migration guidance is tracked in [#147](https://github.com/marionettejs/marionette/issues/147). |
| `MnObject` | The Marionette object class was available as the named `MnObject` export. | `MnObject` remains a named export. | Preserved | Import it directly: `import { MnObject } from 'marionette';`. |
| Reentrant destruction | Calling `destroy()` from `before:destroy` could recurse or repeat teardown for MnObject, Application, and Region. View guarded reentry, but a throwing `before:destroy` left it unable to retry. | MnObject, View, Behavior, and Region guard repeated synchronous teardown as documented by their class contracts. Application destruction is part of its asynchronous lifecycle: compatible repeated calls share an in-flight Promise, and later calls after destruction resolve `true` without restarting teardown. | Changed | Synchronous lifecycle errors stop teardown; later `destroy()` calls do not retry it. For Application, await `destroy()` and treat a rejected current hook as failure; ordinary supersession resolves rather than rejects. |
| Application lifecycle | `Application#start(options)` synchronously fired `before:start` and `start` on every call and returned the Application. Application had no core stop, restart, readiness, running-state, or overlap contract. | `start`, `stop`, `restart`, and `destroy` return `Promise<boolean>`. `true` means the requested target state settled, including idempotent no-op; `false` means a later incompatible operation superseded it. Current hook failures reject. `isRunning()` is true only after startup readiness. Readiness hooks receive an operation context whose `signal` is aborted when their phase is invalidated. | Changed | Await startup before route dispatch or other work that requires readiness. Move asynchronous preparation into a Promise returned by `onBeforeStart` and pass its context signal to cancellable work. Do not treat `false` as failure or add a catch for ordinary cancellation. Remove Toolkit-style `triggerStart` / `finallyStart` overrides; core awaits `onBeforeStart` directly. |
| Application ownership and hierarchy | Core Application had no parent, named-child, root, or child lifecycle contract. Toolkit supplied a separate App class with class/config overloads and per-child lifecycle flags. | Application owns existing child Application instances through one explicit registration path. Name, individual-child, presence, and fresh-snapshot reads expose the public ownership contract without private-field access. Owned children start and stop sequentially with their owner. A conflicting direct child operation cancels owner completion, while descendant startup cannot interrupt owner destruction. Parent destruction stops children before its readiness hook, then destroys them in registration order. A child destroyed directly removes itself from its owner. Conflicts throw `MN0031`, while registration after either lifecycle becomes terminal is a no-op. | Added | Keep Toolkit's established `addChildApp`, `getChildApp(s)`, `removeChildApp`, and `getName` vocabulary, but construct the child explicitly and use `hasChildApp` when allocation must be avoided. Remove `AppClass` configs, `preventDestroy`, `*WithParent` flags, and public parent/root traversal; longer-lived capabilities need a longer-lived owner, and children should receive required collaborators explicitly. Await owner and child lifecycle operations. |
| Application root View and Region ownership | Core Application could construct or receive a Region and proxy `showView`, but it read any `currentView` from that Region and did not coordinate Region or root View teardown with Application lifecycle. | The Application's View is its Region's `currentView`, including Views shown directly through the Region. Stop empties the Region's current View. Destroy also destroys a constructed Region, releases a borrowed Region without destroying it, and clears the Region reference. | Changed | Use a Region instance when an external owner controls the host lifetime; use a selector, Region class, or definition object when the Application should own it. Show a new root View from `onStart` after restart. Views shown directly in a borrowed Region are also emptied when the Application stops. |
| Custom `destroy` overrides | An override could mutate owned state before calling the v4 base `destroy()` method. Reentrant calls and throwing `before:destroy` handlers did not have one consistent retry boundary. | The synchronous base method for MnObject, View, CollectionView, Behavior, and Region establishes its destruction guard before `before:destroy`. Application owns a separate asynchronous lifecycle and returns its destroy Promise. Cleanup performed before delegating remains outside either guarantee. | Changed | Audit every custom `destroy` override. Synchronous owner overrides must preserve the base reentry boundary. Application overrides must return or await the Promise from the base operation and must not recreate a synchronous teardown path. |
| Behavior element retargeting | The undocumented `Behavior#proxyViewProperties()` helper copied the host View's element properties onto the Behavior. | Behaviors share their host's fixed root. `Behavior#setElement()` and `proxyViewProperties()` are removed. | Removed | Choose the host root at construction; do not retarget Behaviors independently. |
| Rendering a destroyed View | A destroyed `View#render` could still resolve `getTemplate` before returning, while destroyed `CollectionView#render` behavior was undocumented. | Destroyed View and CollectionView render calls return the same instance without resolving templates, running render lifecycles, changing DOM, or recreating children. No diagnostic is thrown. | Changed | Render only live View and CollectionView instances. |
| Adding a child to a CollectionView during or after destruction | Base `CollectionView#addChildView` could inspect or manage a supplied View and restart rendering once destruction began. | The base method returns the supplied View before inspecting the View, index, or options or changing events, ownership, DOM, or lifecycle state. | Changed | Add the child to a live CollectionView instead. Custom overrides own their behavior unless they delegate to the guarded base method. |
| Delegating entity events during or after View destruction | Base `View#delegateEntityEvents`, `CollectionView#delegateEntityEvents`, and direct delegation through an attached Behavior could resolve maps and bind new model and collection subscriptions once host destruction began. | The base host methods return the host, and direct `Behavior#delegateEntityEvents` returns the Behavior, without resolving maps or binding handlers once the owning View's destruction starts. Behavior reuse after `Behavior#destroy` while its host remains live is outside this contract. `undelegateEntityEvents` is unchanged. | Changed | No guard is needed for a late base host or attached Behavior delegation call. Use a live View or CollectionView when subscriptions must be established, and undelegate before replacing its model or collection. Custom host and Behavior overrides own their behavior unless they delegate to the guarded base method. |
| Binding UI during or after View destruction | Base `View#bindUIElements`, `CollectionView#bindUIElements`, and direct calls through a retained Behavior could query the retained root element and recreate bound UI once host destruction began. | The base host methods return the host, and direct `Behavior#bindUIElements` returns the Behavior, without resolving callable host UI, querying DOM, or binding View or Behavior UI once the owning View's destruction starts. Behavior reuse after `Behavior#destroy` while its host remains live is outside this contract. `unbindUIElements` and the `MN0023` unbound `getUI` diagnostic are unchanged. | Changed | No guard is needed for a late base host or Behavior binding call. Bind only while the owning View or CollectionView is live; continue to unbind explicitly when cleanup is required. Custom host and Behavior overrides own their behavior unless they delegate to the guarded base method. |
| Framework errors | Framework invariant failures exposed names and prose messages without stable machine identifiers. | `MarionetteError` is a named export and framework invariant failures expose stable `MNxxxx` codes. | Changed | Catch `MarionetteError` and branch on `error.code`; do not parse message prose or legacy documentation URLs. |
| Behavior declarations | Underscore could treat non-array values with numeric `length` as array-like behavior lists. | Arrays are the only list form. Object maps use own enumerable string keys in standard order; inherited, symbol, and non-enumerable keys are excluded, and numeric `length` is an ordinary map entry. | Changed | Use an array for list declarations or an ordinary object for named declarations; do not use generic array-like values. |
| UI map ownership | Underscore could treat a UI map with numeric `length` as array-like and skip its other named keys. A literal own `__proto__` key could change the prototype of normalized or bound UI output instead of remaining a UI entry. | UI binding and map-normalization iteration use own enumerable string keys in standard JavaScript own-key order. Numeric `length` is an ordinary key, and literal own `__proto__` remains an own entry without changing output prototypes. Direct `@ui` lookup still accepts any own declared selector key, including a non-enumerable one. Arrays, sparse arrays, and other array-like values are not supported UI maps. | Changed | Supply iterated UI configuration as an ordinary object. Move intended iterated keys onto that object itself; do not use inherited, symbol, or non-enumerable properties or array-shaped maps. |
| `@ui` reference validation | Missing `@ui` keys could normalize to an `undefined` selector or fail incidentally later. | Every `@ui.<name>` reference must name an own, declared `ui` key; otherwise Marionette throws `MN0018` during normalization. | Changed | Define the `ui` key or replace the reference with a literal selector. |
| `getUI` binding lifecycle | Calling `getUI()` without declared or bound UI elements failed with an incidental `TypeError`. | View, CollectionView, and Behavior throw `MN0023` when `getUI()` is called without a declared `ui` map, before binding, or after unbinding. | Changed | Declare a `ui` map, then render a templated View or call `bindUIElements()` explicitly before `getUI()`; bind again before calling it after unbinding. |
| Handler validation | Missing string handlers and invalid non-function values could be silently omitted during delegation or binding. | Every supplied handler must be a function or a string that resolves to a callable method; otherwise Marionette throws `MN0019` before delegation, binding, or selective unbinding. | Changed | Define or remove the named method, supply the handler as a function, or omit the map when unbinding everything. |
| Region declaration maps | Underscore could treat a Region map with numeric `length` as array-like and skip its other named keys. | Region declarations and `addRegions` use own enumerable string keys in standard order. Inherited, symbol, and non-enumerable keys are excluded, and numeric `length` is an ordinary Region name. Arrays and other array-like values are not supported as Region declaration maps. | Changed | Supply Region declarations as an ordinary object and move intended definitions onto that object itself. |
| Region names | Named Region methods inherited JavaScript property-key coercion, so arrays, objects, and Symbols could become or address Region names incidentally. | View Region names are non-empty strings. The public types require strings. Named registration, lookup, removal, and child operations reject empty names with `MN0032`; unsupported shapes have no guaranteed diagnostic. Explicitly registered string collisions such as `constructor`, `toString`, and `__proto__` remain valid. | Changed | Pass the intended non-empty string name directly; do not rely on property-key coercion. |
| Named Region operations | Required View operations could fail with an incidental `TypeError` when the named Region did not exist. | `showChildView`, `detachChildView`, `getChildView`, and `removeRegion` throw `MN0020` for an unknown Region name; `getRegion` and `hasRegion` remain optional lookups. | Changed | Define the Region before using a required operation, or check it with `hasRegion` or `getRegion`. |
| Region presence queries | `View#hasRegion` delegated to overridable `getRegion`, so querying an unrendered View rendered it before checking the name. | `hasRegion` checks only the View's own registered Region names without rendering, dispatching through `getRegion`, or changing View state, DOM, UI bindings, or lifecycle events. Missing and inherited-only names return `false`; prototype-collision names such as `constructor` return `true` when explicitly registered as own Regions. Destroyed Views return `false` after their Regions are removed. | Changed | Use `hasRegion` for a side-effect-free stored-ownership check. Use a child operation when rendering, override dispatch, and resolving the Region element are intended. |
| Region lookup queries | `View#getRegion` rendered an unrendered View before looking up the Region, so `getRegion(name).show(view)` also rendered the parent implicitly. A non-delegating `getRegion` override could bypass that render. | `getRegion` returns an own registered Region without rendering or changing View state, DOM, UI bindings, or lifecycle events. Child operations now render a live, unrendered View before dispatching through overridable `getRegion`, including non-delegating overrides. Destroyed Views return `undefined` after their Regions are removed. | Changed | Use `getRegion` for a side-effect-free optional lookup. Use `showChildView`, `detachChildView`, or `getChildView` to retain deterministic render-before-lookup behavior; render the parent explicitly before calling a selector Region's `show` directly. |
| Region snapshot queries | `View#getRegions` rendered an unrendered View before returning its Region map. | `getRegions` returns a fresh snapshot of own registered Region names without rendering or changing View state, DOM, UI bindings, or lifecycle events. Inherited keys are excluded, own prototype-collision names remain safe own entries, and destroyed Views return an empty snapshot. | Changed | Use `getRegions` for a side-effect-free ownership snapshot. `emptyRegions` remains a mutator: it renders a live, unrendered View before calling overridable `getRegions` and emptying the returned Regions. |
| Region owner and name queries | Region ownership was maintained privately as `_parentView` and `_name`; reusing a Region instance or occupied name could leave conflicting private ownership records. | `Region#getOwner()` and `Region#getName()` expose the one current registered relationship without rendering, resolving elements, allocating a second registry, or mutating ownership. Standalone and successfully destroyed Regions return `undefined`. Re-adding the same Region under its current owner and name is a no-op. Registration rejects different-owner, different-name, lifecycle-state, and occupied-name conflicts with `MN0030` before committing them. If a lifecycle hook creates a conflict during ordered `addRegions` processing, earlier entries remain registered while the conflicting and later entries do not. | Added | Use these methods instead of reading private fields. Remove an existing named Region before replacing it, and use a fresh Region instance for another owner. For render-time setup, declare a stable Region in `regions` and only show its child from `onRender`. Keep `currentView` and `hasView()` for the Region's owned child rather than adding a second child lookup API. |
| Operating through a destroyed Region | `Region#show` could render and retain a new View after destruction, while `empty`, `reset`, and `detachView` could continue mutating View ownership, DOM, or element caches. | After destruction, `show`, `empty`, and `reset` return the Region without changing it; `detachView` returns `undefined`. `show`, `detachView`, and recursive `destroy` calls are also no-ops while destruction is in progress. `empty` and `reset` remain available during cleanup. | Changed | Use a live Region when the operation must occur. Custom overrides own their behavior unless they delegate to the base method. |
| Region destruction timing | `isDestroyed()` became `true` before `reset()` emptied the Region. | `isDestroyed()` becomes `true` after `reset()` completes, before the `destroy` event. It remains `false` in `before:empty` and `empty` handlers invoked during destruction. | Changed | Use the `destroy` event for completed teardown. Keep cleanup overrides synchronous and avoid recursive `empty` or `reset` calls from their own lifecycle handlers. Discard the Region if cleanup throws; later `destroy()` calls do not retry it. |
| `onShow` | `Region` invoked `onShow(region, view, options)` for its `show` lifecycle event. | The Region `show` event and `onShow` method convention remain supported. | Preserved | No lifecycle rename is required. |
| Native DomApi customization | Applications could replace or partially override Marionette's DomApi globally or per class. | The native DomApi is the default and remains customizable with `setDomApi` or class-level setters. | Documented | Customization applies to `View`, `CollectionView`, and `Region`. |
| EventDelegator customization | DOM event delegation was supplied through Backbone view and jQuery behavior. | Native delegation is the default and can be replaced globally with `setEventDelegator` or per class. A complete adapter implements `delegate({ eventName, selector, handler, rootEl })` and returns an idempotent cleanup function for that exact registration; Marionette invokes it at most once. | Changed | Use the [EventDelegator runtime adapter contract](/docs/upgrade-guide.md#eventdelegator-runtime-adapter). Existing registrations retain their original cleanup when configuration changes; the current adapter is selected on the next delegation pass. |
| Failed View construction cleanup | If `initialize()` or later constructor setup threw after DOM events, Behaviors, or State were initialized, those owned resources could remain attached to a caller-owned element or instance graph. | Constructor errors propagate without rolling back partially completed initialization. | Preserved | Do not depend on inspecting or retaining a partially constructed instance after an exception. See the [synchronous failure boundary](/docs/lifecycle.md#synchronous-failures). |
| Host and Behavior DOM declaration collisions | Marionette flattened every Behavior and host `events` and `triggers` map before asking Backbone to delegate it. Identical event-and-selector keys overwrote earlier declarations according to merge order. | The host and each Behavior own independent delegated listeners. Every matching declaration runs once, without an ordering guarantee among Behavior handlers. | Changed | Remove code that depends on one declaration suppressing another. Give handlers distinct selectors or event types when only one should run, or coordinate the shared action explicitly. |
| View DOM event redelegation | `View#delegateEvents(events?)` and `View#undelegateEvents()` refreshed or removed View and Behavior DOM handlers and returned the View. An explicit map replaced the View's configured `events` while retaining triggers and Behavior handlers. `setElement()` dispatched through the public pair. | The public pair remains available on View and CollectionView with the same explicit-map boundary, Behavior and trigger participation, and chainability. Callable maps and current UI selectors are resolved on each delegation pass, existing handlers are removed first, and calls after destruction starts are no-ops. | Preserved | Continue using the pair when declarative DOM configuration changes at runtime. Do not add destroyed-state guards around these calls. A method override owns cleanup or redelegation unless it calls the base method. |
| `View#remove` | View inherited Backbone's `remove()` method, which removed its element and stopped listeners without running Marionette's complete destroy lifecycle. | The inherited method is intentionally removed. | Removed | Use `destroy()` for terminal cleanup. Use an owning Region's `detachView()` when the live View must be retained for reuse. |
| Imperative `View#delegate` and `View#undelegate` | View inherited Backbone's singular low-level helpers for adding and removing individual delegated handlers. | The inherited helpers are intentionally removed; Marionette owns delegation through declarative maps and the EventDelegator adapter. | Removed | Prefer `events` and `triggers`, then call `delegateEvents()` to refresh them. Use native listeners or a custom EventDelegator only for interactions that cannot be expressed declaratively. |
| Delegated DOM event semantics | Backbone delegated View events through jQuery, including jQuery special-event handling, namespaces, `return false` shorthand, and extra arguments supplied through jQuery triggering. | Delegated handlers receive a native event: `currentTarget` remains the View root and Marionette sets `delegateTarget` to the closest matching descendant, invoking once even when multiple ancestors match. Non-bubbling `mouseenter` is not emulated. Namespaces, `return false`, and extra trigger arguments are not native contracts. | Changed | Use bubbling native events or direct listeners, explicit event methods, and `CustomEvent.detail` as appropriate. See [Native delegation versus jQuery events](/docs/upgrade-guide.md#native-delegation-versus-jquery-events). The optional jQuery DomApi does not change event delegation. |
| Adapter overlay input inheritance | DomApi and EventDelegator overlays could contribute inherited enumerable properties. | DomApi overlays copy own enumerable string and symbol properties and preserve a literal own `__proto__` property without changing the adapter prototype. EventDelegator configuration replaces the complete adapter object instead of overlaying it. | Changed | Move intended DomApi overrides onto the supplied object itself. Provide a complete EventDelegator with a callable `delegate` method. |

## Reading changed and removed rows

The highest-impact migration boundaries are:

- update the package name and imports before addressing runtime behavior;
- keep default-namespace and `Mn.Object` compatibility out of new v5 code;
- explicitly opt into Backbone or jQuery compatibility only where an
  application still needs it;
- resolve `View` and `CollectionView` selector strings before construction,
  while leaving Region selector strings unchanged; and
- audit code that depends on `$el`, jQuery-shaped `view.$()` results, or
  the selected adapter's content replacement and cleanup semantics.

The full ordered migration procedure and remaining before-and-after examples are
tracked in issue #147.

### Await Application readiness

V4 startup completed synchronously, so code commonly dispatched work on the
next line:

```javascript
app.start();
dispatchInitialRoute();
```

V5 awaits a Promise returned by `onBeforeStart`. Await `start()` and dispatch
only when that exact startup reaches running state:

```javascript
const App = Application.extend({
  onBeforeStart(app, options, { signal }) {
    return loadInitialData({ signal });
  }
});

const app = new App();
if (await app.start()) {
  dispatchInitialRoute();
}
```

A `false` result means a later stop, restart, or destroy superseded this startup.
It is not a failure and does not need a `catch`. A current readiness failure
rejects and should use the application's ordinary error path. Marionette aborts
the readiness signal before replacement readiness starts, so pass it to
cancellable work rather than inventing a parallel cancellation hook.


[Canonical source](/docs/markdown/docs/migration-from-v4.md) · [Source identity](/docs/manifest.json)
