A basic principle for writing sane Backbone Marionette code

At 500px, we use Backbone Marionette for the majority of our web application. Marionette presents a fairly straightforward framework which is easy to jump into, but while it’s more well defined than Backbone on its own, it’s not very opinionated on how exactly you should structure your app. Over time, we’ve developed best practices which […]

At 500px, we use Backbone Marionette for the majority of our web application. Marionette presents a fairly straightforward framework which is easy to jump into, but while it’s more well defined than Backbone on its own, it’s not very opinionated on how exactly you should structure your app. Over time, we’ve developed best practices which help us write sane, maintainable Marionette code.

There is one basic principle that should result in mostly reasonable Marionette components:

You should always be able to re-render.

That is, calling render() should result in no change to the DOM. The DOM should always be a reflection of the state of the view instance. To put it another way, if we were to clone the view instance in its current state and then render it on another part of the page, both instances should render identical markup. We will identify a few rules and best practices that follow naturally from this principle and which will help you achieve it.

Just render

This rule could also be called “don’t touch the DOM”. Directly manipulating the DOM (adding/removing classes, changing styles, creating elements, etc.) in your JavaScript leads to a couple of undesirable results:

  1. Your view becomes more difficult to maintain and debug as it’s not clear where DOM state changes might be taking place.
  2. It becomes very easy to end up with a mismatch between what the DOM is showing and the stored state of your view.

When this happens, some of the state of your component is actually stored only in the DOM, and re-rendering the component will cause this information to be lost.

How can we avoid this? Do as much as you possibly can in the template, and if absolutely necessary, manipulate the DOM only in the onRender function (which is called by Marionette after rendering). You can make use of templateHelpers to make this a bit easier. Any time you need to change the state of the DOM, you should modify a view instance variable and re-render the view by calling render().

Bad:

https://medium.com/media/f1117081011fa01a3f82baeba0aab9da/href

Good:

https://medium.com/media/1fafddd39ca728b898f4a0159936ade6/href

Don’t save state in the DOM

You should avoid storing state in the DOM at all costs. The DOM should reflect the state of the view instance, and you should never need to query the DOM for information. If some state is stored exclusively in the DOM (in the form of “selected” elements, checkbox states, user re-ordered items, etc.), then calling render() will cause us to lose that state, breaking our basic principle.

Break down views into small logical components

There are a lot of obvious and oft-mentioned benefits to breaking code up into smaller chunks. It’s easier to read and maintain, promotes code reuse, is easier to test, helps parallelize development… the list goes on. As it relates to our rule of always rendering instead of directly updating the DOM, it helps us keep the scope of renders under control and render only what is required.

Use the Backbone router

If your app has distinct pages, don’t overcomplicate things by listening for events and updating the page piece at a time — just use the router. Even if you have some common elements, like navigation, you can pull those into their own view components and include them in each page. You can think of this as applying the Just render rule to the entire page. It’s much easier to follow and understand your code if there is one, clean path to building each page or screen.

Bad:

https://medium.com/media/7957d82c93e5e078bb03bd0b13eec177/href

Good:

https://medium.com/media/c7a83f997ee42eefec2513f1d58aae17/href

Caveats

Of course, there are always exceptions to the rule. It is not always possible to avoid updating the DOM outside of render(). For example, sometimes re-rendering will result in a “jump” in the UI, or perhaps the loss of a scroll position. Rendering for each change also means you can’t use any transitions. Try to keep these situations limited to small sub-views in order to contain the damage, and it doesn’t hurt to leave a comment explaining why you’re breaking the general rule of thumb.

Wrap up

To recap:

  • Change the DOM by updating instance variables and calling render()
  • Don’t update the DOM piecemeal throughout your view
  • Do as much as possible in the template, use onRender if necessary
  • Don’t save state in the DOM
  • Break down views into small sub-views
  • Use the router

If you follow theses basic guidelines, and always ask yourself “if I re-rendered this view right now, would anything change?”, you’ll be on your way to writing some reasonably maintainable Backbone Marionette views. They seem straightforward, but even when you know them, they’re easy to ignore when deadlines are looming and crunch time is upon you. Your future self (and coworkers) will thank you if you resist that urge.


A basic principle for writing sane Backbone Marionette code was originally published in 500px Engineering Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Source: 500px