# Allow newlines inside {% %} tags

**URL:** <https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040>\
**Category:** Django Internals\
**Created:** [October 29, 2024, 8:10am UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040 "2024-10-29T08:10:11Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![boxed](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/boxed/32/11975_2.png) [@boxed](https://forum.djangoproject.com/u/boxed)\
**Post date:** [October 29, 2024, 8:10am UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/1 "2024-10-29T08:10:11Z")

</div>

After a discussion on mastodon: [Jeff Triplett: &quot;@boxed @anze3db@fosstodon.org Agreed. I wish we c…&quot; - Mastodon](https://mastodon.social/@webology/113386394755054612)

That newlines inside `{% %}` is not allowed makes some formatting of templates more ugly and bad for git blame. It also causes problems for beginners, as the error message when you do this is not great.

I suggest allowing newlines.

```py
tag_re = re.compile(r"({%.*?%}|{{.*?}}|{#.*?#})")

```

could maybe be changed to

```py
tag_re = re.compile(r"({%.*?%}|{{.*?}}|{#.*?#})", re.DOTALL)

```

There is a backwards compatibility issue in that

```html
{% 
%}

```

currently renders `{% %}` as a string in the template, while it would be an error with this change.

Maybe this needs to be an option on the template settings, with a deprecation warning if you haven’t turned it on, and change startproject to insert that setting by default.

---

<div class="post-metadata">

**Author:** ![cb109](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/cb109/32/15216_2.png) [@cb109](https://forum.djangoproject.com/u/cb109)\
**Post date:** [October 29, 2024, 8:28am UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/2 "2024-10-29T08:28:36Z")

</div>

Yes please! Especially with scoped includes, for me they can get very long and very hard to read:

```auto
{% include "client/visitorcount/store_visitorcount_overall_statistics.html" with overall_statistics=overall_statistics overall_statistics_json=overall_statistics_json selected_store=selected_store user_stores_with_sensors=user_stores_with_sensors chart_height=weeks_comparison_chart_height only %}

```

Would rather have this look like:

```auto
{% 
  include 
    "client/visitorcount/store_visitorcount_overall_statistics.html" 
  with 
    chart_height=weeks_comparison_chart_height 
    overall_statistics_json=overall_statistics_json 
    overall_statistics=overall_statistics 
    selected_store=selected_store 
    user_stores_with_sensors=user_stores_with_sensors 
  only 
%}

```

---

<div class="post-metadata">

**Author:** ![Lily-Foote](https://avatars.discourse-cdn.com/v4/letter/l/c37758/32.png) [@Lily-Foote](https://forum.djangoproject.com/u/Lily-Foote)\
**Post date:** [October 29, 2024, 8:31am UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/3 "2024-10-29T08:31:27Z")

</div>

After seeing this example, it’s a clear +1 to me.

---

<div class="post-metadata">

**Author:** ![nanorepublica](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/nanorepublica/32/14211_2.png) [@nanorepublica](https://forum.djangoproject.com/u/nanorepublica)\
**Post date:** [October 29, 2024, 9:22am UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/4 "2024-10-29T09:22:45Z")

</div>

I assume this would affect all usages of `{% %}`?

If so a possible side effect is that prettier formatting would no longer break Django templates.

+1 from me

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [October 29, 2024, 10:00am UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/5 "2024-10-29T10:00:02Z")

</div>

+1 from me.

This feature has been looked at several times in the past. @thibaudcolas 's django-developers post from 2 years ago has links to the past attempts: [https://groups.google.com/g/django-developers/c/rrDlBF4-uY4/m/JlcmjWC\_AwAJ](https://groups.google.com/g/django-developers/c/rrDlBF4-uY4/m/JlcmjWC_AwAJ)

I think there’s still a question of feasibility. It’s not as simple as just changing the one regex 😉 .

There’s also an open ticket from @theorangeone about that particular regex’s possible poor performance: [#35675 (Reduce impact of parsing crafted templates with repeat tags) – Django](https://code.djangoproject.com/ticket/35675) . Opening it to newlines will only make that possibility worse, but maybe that’s a separate concern.

> [@boxed](#):
>
> Maybe this needs to be an option on the template settings, with a deprecation warning if you haven’t turned it on, and change startproject to insert that setting by default.

I think this will be the case, yes.

> [@nanorepublica](#):
>
> If so a possible side effect is that prettier formatting would no longer break Django templates.

By “prettier”, I assume you mean [Prettier](https://prettier.io/), rather than general formatting tools that make things prettier 😉

---

<div class="post-metadata">

**Author:** ![thibaudcolas](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/thibaudcolas/32/3956_2.png) [@thibaudcolas](https://forum.djangoproject.com/u/thibaudcolas)\
**Post date:** [October 29, 2024, 10:09am UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/6 "2024-10-29T10:09:49Z")

</div>

+1 – thank you for finding that thread and tagging me, I’d still love that, just ran out of steam last time I tried to make it happen. There wasn’t any particular reason, just got busy with other things.

---

<div class="post-metadata">

**Author:** ![nanorepublica](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/nanorepublica/32/14211_2.png) [@nanorepublica](https://forum.djangoproject.com/u/nanorepublica)\
**Post date:** [October 29, 2024, 10:12am UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/7 "2024-10-29T10:12:09Z")

</div>

😄 yes I do mean the tool, not just things being prettier 😂

---

<div class="post-metadata">

**Author:** ![boxed](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/boxed/32/11975_2.png) [@boxed](https://forum.djangoproject.com/u/boxed)\
**Post date:** [October 29, 2024, 10:30am UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/8 "2024-10-29T10:30:42Z")

</div>

I see there’s an existing PR linked in that mail thread: [Multiline templates by loic · Pull Request #2556 · django/django · GitHub](https://github.com/django/django/pull/2556) which honestly looks pretty good. I think we’d want a bit more paranoia when it comes to backwards compatibility like I mentioned above though.

Or am I the one being overly paranoid now?

---

<div class="post-metadata">

**Author:** ![CodenameTim](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/codenametim/32/31507_2.png) [@CodenameTim](https://forum.djangoproject.com/u/CodenameTim)\
**Post date:** [October 29, 2024, 12:25pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/9 "2024-10-29T12:25:13Z")

</div>

I wonder if we could use `django-upgrade` to solve that backwards compatibility issue?

---

<div class="post-metadata">

**Author:** ![boxed](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/boxed/32/11975_2.png) [@boxed](https://forum.djangoproject.com/u/boxed)\
**Post date:** [October 29, 2024, 12:39pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/10 "2024-10-29T12:39:28Z")

</div>

How do you mean solve it?

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [October 29, 2024, 12:46pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/11 "2024-10-29T12:46:24Z")

</div>

> [@CodenameTim](#):
>
> I wonder if we could use `django-upgrade` to solve that backwards compatibility issue?

django-upgrade isn’t in the templates game, but Djade is 😉

> [@boxed](#):
>
> How do you mean solve it?

We could do this to “escape” any existing multiline non-tags:

```diff
-{% This isn't meant to be a tag
+{{ '{' }}% This isn't meant to be a tag
-nor this %}
+nor this %{{ '}' }}

```

Happy to add this to Djade, if need be.

But then that doesn’t really fix adoption.

Perhaps a better approach would be a Python-style “future load” to initially opt into the multiline tag feature: `{% load multiline from future %}`. We can flag the deprecation warning for any templates that contain unmatched `{%` / `{{` / `{#`. Then a later version makes the behaviour default.

---

<div class="post-metadata">

**Author:** ![CodenameTim](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/codenametim/32/31507_2.png) [@CodenameTim](https://forum.djangoproject.com/u/CodenameTim)\
**Post date:** [October 29, 2024, 12:47pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/12 "2024-10-29T12:47:11Z")

</div>

```auto
{% 
%}

```

Would be automatically transformed to

```auto
{% verbatim %}
{% 
%}
{% endverbatim %}

```

---

<div class="post-metadata">

**Author:** ![boxed](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/boxed/32/11975_2.png) [@boxed](https://forum.djangoproject.com/u/boxed)\
**Post date:** [October 29, 2024, 1:00pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/13 "2024-10-29T13:00:29Z")

</div>

I think you want `{% templatetag openblock %}` et al.

---

<div class="post-metadata">

**Author:** ![boxed](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/boxed/32/11975_2.png) [@boxed](https://forum.djangoproject.com/u/boxed)\
**Post date:** [October 29, 2024, 1:02pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/14 "2024-10-29T13:02:32Z")

</div>

Changing the parsing part way through the template seems fraught!

I would object to warning based on the complaints that the templates are already too slow. If we do the warning, surely they would be even slower.

---

<div class="post-metadata">

**Author:** ![emma](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/emma/32/21174_2.png) [@emma](https://forum.djangoproject.com/u/emma)\
**Post date:** [October 29, 2024, 6:25pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/15 "2024-10-29T18:25:39Z")

</div>

a clear +1 from me.

I’m also for some sort of tooling that would detect that during the upgrade process but if `django-upgrade` is not that tool, I’m not for asking people to run X different tools to upgrade something

---

<div class="post-metadata">

**Author:** ![boxed](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/boxed/32/11975_2.png) [@boxed](https://forum.djangoproject.com/u/boxed)\
**Post date:** [October 29, 2024, 7:27pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/16 "2024-10-29T19:27:48Z")

</div>

Is there any such tool for anything in Django today? I can’t think of any…

---

<div class="post-metadata">

**Author:** ![mightym](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/mightym/32/24879_2.png) [@mightym](https://forum.djangoproject.com/u/mightym)\
**Post date:** [October 29, 2024, 11:04pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/17 "2024-10-29T23:04:48Z")

</div>

+1 from me! That would be a great improvement eg on scoped includes.

---

<div class="post-metadata">

**Author:** ![tom](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tom/32/6818_2.png) [@tom](https://forum.djangoproject.com/u/tom)\
**Post date:** [October 29, 2024, 11:25pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/18 "2024-10-29T23:25:20Z")

</div>

I also have been wanting this for many years. At work we use a lot of includes and other tags with multiple often quite long strings passed in (sometimes template names and so).

I think there’s clear rationale and consensus for it and a PR that could hopefully be revived, so I think someone needs to step up and write the ticket and open a new PR.

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [October 29, 2024, 11:29pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/19 "2024-10-29T23:29:05Z")

</div>

> [@boxed](#):
>
> Changing the parsing part way through the template seems fraught!

It doesn’t need to be part-way through. Like [Python’s ` __future__ ` rules](https://docs.python.org/3.12/reference/simple_stmts.html#future-statements), we can require that the special tag appears near the top of the file.

The change here is in tokenization, which is technically the step before parsing.

> [@boxed](#):
>
> I would object to warning based on the complaints that the templates are already too slow. If we do the warning, surely they would be even slower.

The warning needs only be done once when loading the template. After tokenizing, when the old mode is in effect, we could check all text nodes for `{%`, `{{`, or `{#`, and warn for each occurrence. I would expect this to be a small cost compared to the existing tokenization run.

The “templates are slow” statement/meme normally refers to render time, not load/parsing time, as far as I’ve heard. So, I don’t think such a check would impact the main cost.

(I have an open proposal to help optimize render time a bit: [Add \_\_slots\_\_ to template Node classes (only)](https://forum.djangoproject.com/t/add-slots-to-template-node-classes-only/35387) .)

---

<div class="post-metadata">

**Author:** ![boxed](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/boxed/32/11975_2.png) [@boxed](https://forum.djangoproject.com/u/boxed)\
**Post date:** [October 29, 2024, 11:52pm UTC](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040/20 "2024-10-29T23:52:04Z")

</div>

> **[Comparing django:main...boxed:multiline-template-tags · django/django](https://github.com/django/django/compare/main...boxed:django:multiline-template-tags)**
>
> The Web framework for perfectionists with deadlines. - Comparing django:main...boxed:multiline-template-tags · django/django

A rough first draft.

[Next page](https://forum.djangoproject.com/t/allow-newlines-inside-tags/36040.md?page=2)
