Previous article presented forms and validation in Play Framework. But we didn't described how to display and format forms in the templates.
Data Engineering Design Patterns

Looking for a book that defines and solves most common data engineering problems? I wrote
one on that topic! You can read it online
on the O'Reilly platform,
or get a print copy on Amazon.
I also help solve your data engineering problems 👉 contact@waitingforcode.com 📩
This time we'll do it. At the begin we'll show how to display simple forms. After that, we'll focus on more customized presentation.
Display forms in Play Framework templates
Forms can be rendered thanks to form helpers. All helpers are located in Scala's package called view/helper and they are represented by .scala.html files. So, if you want to create a form, we need to use @form helper which content looks like:
@(action: play.api.mvc.Call, args: (Symbol,String)*)(body: => Html)
Two other useful elements in the case of our register form are helpers to generate text and password inputs:
@(field: play.api.data.Field, args: (Symbol,Any)*)(implicit handler: FieldConstructor, lang: play.api.i18n.Lang) @inputType = @{ args.toMap.get('type).map(_.toString).getOrElse("text") } @input(field, args.filter(_._1 != 'type):_*) { (id, name, value, htmlArgs) => }
and
@(field: play.api.data.Field, args: (Symbol,Any)*)(implicit handler: FieldConstructor, lang: play.api.i18n.Lang) @input(field, args:_*) { (id, name, value, htmlArgs) => }
To construct form and its fields, we need to use information about created play.data.Form instance (User in our case). We can also specify supplementary HTML attributes, directly after the field object:
@helper.form(action = routes.UserController.registerSubmit()) { @helper.inputText(userForm("login"), 'id -> "userLogin", 'fieldImportancy -> "VERY-HIGH", 'size -> 10) @helper.inputPassword(userForm("password")) @helper.inputText(userForm("birthday")) }
This code should generate following HTML:
Customize forms view in Play Framework
As you could see, generation of form HTML is quite simple. But it may be not adapted to your expectations. It's why Play Framework allows also some of customization. In our case, we want to override inputs creation to not show
tags. To make it possible, we need to create a field constructor that takes all element information (validation errors, field data) and put them together inside one template. We'll make this construct in views/form/helpers directory:@(elements: helper.FieldElements)@elements.input
@elements.errors.mkString(", ")
The name of this file will be inputFieldHelper.scala.html. Now we can come back to our register.scala.html and import new form field constructor:
@(userForm: play.data.Form[User]) @import helper._ @implicitField = @{FieldConstructor(helpers.form.inputFieldHelper.render)} @main("Welcome to register page") { @helper.form(action = routes.UserController.registerSubmit()) { @helper.inputText(userForm("login"), '_label -> "Login", 'id -> "userLogin", 'fieldImportancy -> "VERY-HIGH", 'size -> 10) @helper.inputPassword(userForm("password"), '_label -> "Password") @helper.inputText(userForm("birthday"), '_label -> "Birthday") } }
As you can see, they're some new things. The first is the import of all members of helper._ package. Thanks to it, we can retrieve FieldConstructor. After that, we override default constructor by the constructor of our template. Now, all fields will be generated with it. In additionally, we added some of improvements for the form, as label customization (note that they have to be named _label, otherwise they would be considered as HTML field attributes). After the compilation and execution of the page, we should receive the register form based on our inputFieldHelper template:
In this article we could see how to generate the view of forms. The first part covered a standard Play method. But because generated forms weren't adapted to our expectaitions, we needed to override this behaviour with new default field constructor. This technic was presented in the second part of the article.
Consulting

With nearly 16 years of experience, including 8 as data engineer, I offer expert consulting to design and optimize scalable data solutions.
As an O’Reilly author, Data+AI Summit speaker, and blogger, I bring cutting-edge insights to modernize infrastructure, build robust pipelines, and
drive data-driven decision-making. Let's transform your data challenges into opportunities—reach out to elevate your data engineering game today!
👉 contact@waitingforcode.com
đź”— past projects