Making view templates as dumb as possible

When we design products, we start with a set of user needs and translate them into screens (and other things) that meet those needs.

We then translate those screens into frontend code. Then we might try and findd the best way to split up the pieces into components.

Once that’s done, it’s time to integrate data and logic into the templates for the user to actually interact with.

The object that contains the data and logic is called a view model — it’s a model designed for the view.

We can’t design a view-model without first understanding the needs of the view. And we can’t understand the needs of the view without first understanding the visual and interaction design requirements etc.

Sometimes, a view model is badly designed or neglected. By that I mean it exposes too much low-level logic which makes templates difficult to work with.

For example, take a welcome message consisting of some text and a link. And it renders differently depending on the user’s circumstance.

  • When the user isn’t signed in: Welcome — [sign in]
  • When the user is signed in and the service knows their name: Welcome Adam — [sign in]
  • When the user is signed in but the service doesn’t know their name: Welcome back — [sign in]

Consider the following template:

{% if isSignedIn %}
  {% if firstName and lastName %}
    <p>Hello {{firstName}} {{lastName}} - <a href="{{signOutLink.href}}">{{signOutLink.text}}</a></p>
  {% else %}
    <p>Welcome back - <a href="{{signOutLink.href}}">{{signOutLink.text}}</a></p>
  {% endif %}
{% else %}
  <p>Welcome - <a href="{{signInLink.href}}">{{signInLink.text}}</a></p>
{% endif %}

There’s no need for separate first and last name properties because they’re styled the same. And there’s no need to check whether the user is signed in.

See this instead:

<p>{{message}} — <a href=”{{link.href}}”>{{link.text}}</a></p>

The view model has been designed from the outside in. It contains just what the template needs in order to render itself.

As there’s always a message and a link, there’s no need for conditionality.

This template is 1 line compared to the original line and consists mostly of HTML which is far easier to read.

This template is dumb and means complex logic can live somewhere more appropriate. And as the logic lives outside the template, it can be unit tested.

Next, I’ll cover some other common scenarios where we can make our templates dumb.

# 1. Views shouldn’t know where data comes from

If, for example, your view displays a message of ‘Hello Adam’ the view model should be:

{ message: "Hello Adam" }

The template would be:


The message may consist of text from resource files and the database but the view shouldn’t need to worry about that.

# 2. Views shouldn’t know why something should be displayed

If, for example, a message is only displayed when the user is signed in, the view model should be:

// when signed in
{ showMessage: true, message: "Hello Adam"}

Or this:

// when not signed in
{ showMessage: false }

The template would be:

{% if showMessage %}
{% endif %}

The template shouldn’t be concerned about why the message is shown or not.

# Summary

If we’re not careful, our templates can become unnecessarily complex, consisting of logic that templates shouldn’t really have to worry about.

Templates shouldn’t have to infer display logic from multiple other properties and they shouldn’t have to care where its data comes from.

Templates should be easy to read and consist mostly of HTML with just enough information in order to support any display logic.

That keeps templates dumb and easy to maintain. It also means complex logic can be united tested and live in files that are suited to such logic.