{{craftSnippets}}
Home Articles Plugins Starter theme About
  • Home
  • Articles
  • Namespacing forms in Craft CMS
Posted on Apr 26, 2020 by Piotr Pogorzelski

Namespacing forms in Craft CMS

If you want to use specific HTML form in multiple parts of a single page - to avoid potential problems you need to use "namespace" Twig tag.

Sometimes we need to use specific HTML form in multiple parts of single page. For example, the search form might be placed in the desktop menu, while on mobile it would appear only in the popup. Or we might have carousel with for loop listing through matrix field containing company employees, where each person has his/her own contact form.

Following DRY principle, we should create only a single form Twig component and include it in multiple places. This can, unfortunately, lead to problems, such as:

  • Multiple occurrences of inputs with same id attribute and labels with same for attribute might cause conflicts. When the user clicks on the label, the browser focuses cursor on input with id matching labels for. If there are multiple inputs with the same id, first occurring input will get focused. Which might not be input from our form. Not to mention that elements with duplicate id will cause w3c validation errors.
  • Sometimes we might want to fill the form with data it sent upon submitting. This is commonly used in search forms, where we want to have the ability to modify search text after submitting the form. If we, however, have multiple forms using the same component, all forms would be filled with submitted data, which might not be the desired result.

The solution to these problems is to namespace the form. By doing that, we ensure that each instance of the form component has unique values of potentially problematic attributes.

Manually namespacing form #

First, let's do this the hard way. Here's a very simple form. It has getQueryParam method inside input value attribute. getQueryParam returns query string with a specific key. name attribute content is same as the parameter of getQueryParam - this means that when the form is submitted with specific text, it will retain this text after page reloads.

<form action="">
<label for="someId">Our label</label>
<input type="text" id="someId" name="someName" value="{{craft.app.request.getQueryParam('someName')}}">
</form>

Now, lets append {{namespace}} variable to for, id and name attributes, and also to getQueryParam parameter.

<form action="">
<label for="{{namespace ?? null}}someId"></label>
<input type="text" id="{{namespace ?? null}}someId" name="{{namespace ?? null}}someName" value="{{craft.app.request.getQueryParam((namespace ?? null)~'someName')}}">
</form>

Each time we include this form component, we can provide it with a unique namespace variable. Thanks to using the null coalescing operator (??) which will replace variable with null if it is missing, we might also use it without namespace variable.

{# first form #}
{% include 'our_form' with {namespace: 'first-form'} only %}

{# second form #}
{% include 'our_form' with {namespace: 'second-form'} only %}


{# not namespaced #}
{% include 'our_form' %}

This method works, but appending variables everywhere makes form more complicated and its code harder to read. Fortunately, Craft allows us to namespace forms more efficiently.

Namespace Twig tag #

Enter the namespace Twig tag. It will automatically append namespace to id, for and name HTML attributes.

{% namespace 'something' %}
<form action="">
<label for="someId"></label>
<input type="text" id="someId" name="someName" value="{{craft.app.request.getQueryParam('someName')}}">
</form>
{% endnamespace %}

This will result with form rendered like this:

<form action="">
<label for="something-someId"></label>
<input type="text" id="something-someId" name="something[someName]" value="">
</form>

As you can see, id and for values changed into something-someId. name content changed into something[somename], which means that query string sent by this form will be nested.

But what about getQueryParam function? Its argument needs to be namespaced too. We could potentially use namespaceInputName filter for its argument - like this:

{{craft.app.request.getQueryParam('someName'|namespaceInputName)}}

This would change someName into something[someName]. Note that we don't need to provide a parameter to namespaceInputName filter. If we use it within namespace tag, it will use namespace provided to this tag by default.

There is hovewer one problem - for nested query strings, getQueryParam accepts syntax like something.someName instead of something[someName]. We can use simple macro to walk around that problem (thanks to Brandon Kelly for pointing this out on github):

{% macro namespaceParam(param) %}
    {{- param|namespaceInputName|replace('/[\\[\\]]+/', '.')|trim('.') -}}
{% endmacro %}

{{ craft.app.request.getQueryParam(_self.namespaceParam('someName')) }}

This macro will replace [ and ] characters with . and strip last . from end of string. So, something[someName] will change into something.someName, which can be used in getQueryParam() method.

Our final, properly namespaced form might look like that:

{% macro namespaceParam(param) %}
    {{- param|namespaceInputName|replace('/[\\[\\]]+/', '.')|trim('.') -}}
{% endmacro %}
<form action="">
<label for="someId"></label>
<input type="text" id="someId" name="someName" value="{{ craft.app.request.getQueryParam(_self.namespaceParam('someName')) }}">
</form>

Well, except a bit of tinkering with craft.app.request.getQueryParam, it looks just like regular form. That's because we don't actually need to use namespace tag inside form component - we can use it when we include it using {% include %}.

{# first form #}
{% namespace 'first-form' %}
	{% include 'our_form'%}
{% endnamespace %}

{# second form #}
{% namespace 'second-form' %}
	{% include 'our_form'%}
{% endnamespace %}

Namespecing CSS styles #

In Craft 3.5 (which is currently yet to be released), namespace tag will gain additional functionality. It will be able to namespace CSS styles.

Let's say that HTML component that you want to include in your page has its own <style> tag. Normally, such styles would pose a danger of messing with other styles of our site, especially if they use some generic class names or even style HTML tag directly.

namespace tag will allow us to prevent such problems. If used with keyword withClasses, it will be able to namespace class and id attributes of HTML elements, as well as style declarations. Thanks to that, these styles will be contained to the specific component.

So, such code:

{% namespace 'foo' withClasses %}
<style>
  .title { font-weight: bold; }
</style>
<div class="title"></div>
{% endnamespace %}

Will be rendered as:

<style>
  .foo-title { font-weight: bold; }
</style>
<div class="foo-title"></div>

TAGS:
#Craft CMS 3 #Templating
Table of contents:
  • Manually namespacing form
  • Namespace Twig tag
  • Namespecing CSS styles
Let the expert handle
your Craft CMS project.
70 Basic icons by Xicons.co
piotrpog@protonmail.com
Copyright © 2025 Piotr Pogorzelski
piotrpog@protonmail.com      LinkedIn profile      Github profile