Strict locals in Slim / Haml partials in Rails
Tags: rails, slim, erb
Published: 2024-02-22 17:13:09

When I saw the announcement about [partial template strict locals](https://guides.rubyonrails.org/7_1_release_notes.html#allow-templates-to-set-strict-locals) in Rails 7.1, I was excited but it took me a long time to realize that this is not an ERB-only feature but that it should actually work in any template language, including my favorite – [Slim](https://slim-template.github.io/)! I even remember trying it back then but failing, probably due to an incorrect syntax. While there are [many](https://masilotti.com/safer-rails-partials-with-strict-locals/) [nice](https://blog.kiprosh.com/allow-template-to-set-strict-locals/) [tutorials](https://www.driftingruby.com/episodes/strict-locals) explaining template strict locals out there, they are all ERB-centric so let’s take a look how this feature can be used in Slim or Haml templates, respectively.

### Preparations

Suppose we want to render a partial template from a main one one, to show an important message:

```slim
/ main.html.slim

= render "notice"
```

```slim
/ _notice.html.slim

h1
  ' Note:
  = message
```

If we run this code, it will fail with an ugly and misleading error message:

> undefined local variable or method `message` for an instance of `#<...>`
> Hint: `message` is probably misspelled.

saying that we probably misspelled `message` which we did not - we forgot to pass the local variable in the first place.

### Adding a magic comment

Let’s make the partial template recognize our local variable using a **strict locals magic comment**:

```slim
/ _notice.html.slim

/# locals: (message:)

h1
  ' Note:
  = message
```

Note that the exact syntax is important here: it must be a [comment](https://rubydoc.info/gems/slim/frames#code-comment) (denoted by the slash `/` character in Slim), then a hash (`#`), a space, then `locals:`, another space and finally something that resembles a method arguments definition with keyword arguments specifying the needed local variables. The reason this syntax is so strict is that it gets matched by a [regular expression](https://github.com/rails/rails/blob/9f1dec2ea5155205a880f6e6e232cf9ea6da2d8c/actionview/lib/action_view/template.rb#L11) deep in the Action View templates processing code.

In case of a [HAML template](https://haml.info/), the syntax differs only by the magic comment leading character, it must be `-#` instead of `/`:

```haml
/ _notice.html.haml

-# locals: (message:)

%h1 Note!
  = message
```

Note that both magic comments are code comments, i.e. they are _not_ output to the final HTML.

Once we have this magic comment in place, we get a much better error:

> missing local: :message

and the stack trace leads us to the proper place (to the line with `render` in the main template) right away.

And of course, all other [strict locals goodies](https://guides.rubyonrails.org/7_1_release_notes.html#allow-templates-to-set-strict-locals) work, too, such as the default values or prohibiting any locals.

### Closing thoughts

Up till now, we commonly used the [`local_assigns` hash](https://guides.rubyonrails.org/action_view_overview.html#using-local-assigns) to notify the reader that our variable is to be passed from the outside and to set its default value. We think that strict locals make this hash a little obsolete as they serve very similar purposes with a nicer syntax.

Although we still [prefer](https://dev.to/nejremeslnici/from-partials-to-viewcomponents-writing-reusable-front-end-code-in-rails-1c9o) View Components for our most sophisticated view template blocks, we will definitely use Rails partials with more pleasure since we can be sure now to write them in a safer way. 👍🏻