Migration

5.0.0-beta.1 · Published beta · b06750c5. Published on npm. Match your installed version.

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.

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.
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.
Default export namespace Namespace-style default import usage was supported. A default namespace export is not supported. Removed Use named imports. See Quick start; final migration guidance is tracked in #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.
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.
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() 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.
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.
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.
$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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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. 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.
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. 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:

app.start();
dispatchInitialRoute();

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

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.