What HTML to use for a read-only form?

Thomas King - Nov 27 '23 - - Dev Community

Front-end developers will always have come to some point in their career where they have to deal with forms. Creating a registration form, a login form, a form to gather user information, etc. The examples are numerous. Creating such forms is often something that most developers know how to do. Using labels and inputs, applying the correct styling or classes, and woosh, there is a form that the user can fill in.

The challenge is when that same form needs to be displayed in a read-only way. Labels and inputs speak for themselves, but what should you choose to display them? A good example would be in a CRUD application (an acronym for Create, Read, Update, and Delete). What often happens is that the HTML of the "create" or "edit" form is taken, but the form controls are then replaced with span or p elements. Although this works, it's not the best practice when accessibility (a11y) comes to mind.

An example of how it is often done:

<div class="user-settings-form">
  <div class="user-data">
    <div class="form-field">
      <label for="firstName">First name</label>
      <span id="firstName">John</span>
    </div>
    <div class="form-field">
      <label for="lastName">Last name</label>
      <span id="lastName">John</span>
    </div>
  </div>
  <div class="user-address">
    <div class="form-field">
      <label for="addressLine1">Address line 1</label>
      <span id="addressLine1">123 Maple street</span>
    </div>
    <div class="form-field">
      <label for="addressLine2">Address line 2</label>
      <span id="addressLine2">Anytown, PA 17101</span>
    </div>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

One thing is that label elements are used to describe the corresponding form control (specifically label elements). This can be done either by using the for attribute or by putting the form control inside the label element itself. But since there is no longer a form control, the use of the label element should be discouraged.

Another thing is that you should strive for semantic HTML. Writing semantic HTML means using HTML elements to structure your content based on each element's meaning, not its appearance. And by keeping the structure of the existing "create" or "edit" form and replacing the form controls with span or p elements, that doesn't really fit. Especially div and span elements convey no meaning at all. It also makes your HTML quite bulky.

What would be a good HTML structure?

Let's keep the previous two things in mind and have a look at what a read-only form actually contains. In general terms, it's a list of questions and answers. E.g., the answer to the question "first name" is "John". 
When digging through the (rather big) list of semantic HTML elements, we see the dl element ("description list"). The definition for a dl element according to mdn is the following:

The <dl> element encloses a list of groups of terms (specified using the <dt> element) and descriptions (provided by <dd> elements). Common uses for this element are to implement a glossary or to display metadata (a list of key-value pairs).

A "list of key-value pairs" is what we need here. Using a description list not only makes our form semantic but also makes it quite flat. This is a huge benefit when we want to keep style and template separate, because it will be the stylesheet that will tell how the form will look and not the other way around.

So coming from the bulky code we had for the form in the beginning, this can be transformed to:

<dl>
  <dt>First name</dt>
  <dd>John</dd>
  <dt>Last name</dt>
  <dd>Doe</dd>
  <dt>Address line 1</dt>
  <dd>123 Maple street</dd>
  <dt>Address line 2</dt>
  <dd>Anytown, PA 17101</dd>
</dl>
Enter fullscreen mode Exit fullscreen mode

Much cleaner, flatter, and less bulky than the previous code. But I hear you say, "Now I should write more code in my stylesheet." That's true, but that's also where it belongs. The HTML now only conveys the meaning of the form; how it should look in the browser is a concern for the stylesheet.

Note: You can still use classes to make your styling more specific. In an Angular component, for example, I would leave those out and style the dl, dt, and dd elements directly due to ViewEncapsulation. Leaving out those classes makes it even cleaner.

To keep a 2-column layout for the form using the new HTML structure, we could harness the power of CSS grids, and the stylesheet could look like this (note: .user-form is the class given to the dl element):

.user-form {
  display: grid;
  gap: 0.5rem;
  grid-template-columns: 100px auto;
}

.user-form > dt,
.user-form > dd {
  margin: 0;
}

.user-form > dt {
  text-align: right;
  font-weight: bold;
}
Enter fullscreen mode Exit fullscreen mode

In the original piece of code, there was also a difference between the "user data" and the "user address". That is no longer visible in the HTML now, but if you want to distinguish between them in the stylesheet, you can do so by using the :nth-of-type pseudo class:

.user-form > dt:nth-of-type(2),
.user-form > dd:nth-of-type(2) {
  margin-bottom: 1rem;
}
Enter fullscreen mode Exit fullscreen mode

You have reached the end of this blog. I hope this guide will give you better insights into how to structure read-only forms in HTML.

If you have any questions, feel free to contact me!

. . . . . . . . .