Standard HTML Components

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Jakarta Faces 4.1 (API jakarta.faces-api 4.1.2, part of Jakarta EE 11, Java SE 17 or later), with Eclipse Mojarra 4.1.17 and Apache MyFaces Core 4.1.4. Faces 4.0 (Jakarta EE 10) behaves the same for everything below except where What Changed Between Versions says otherwise. The code and the HTML shown were run for real: the pages were deployed in a scratch Bookshelf WAR on Apache Tomcat 11.0.26 with Weld 6.0.4.Final (Java 21, English locale, project stage Development), requested with curl, and the markup quoted is what came back, with session ids removed (checked on 2026-10-08). Every statement about the tags is taken from the Jakarta Faces 4.1 VDL documentation, the specification and the Jakarta EE Tutorial, and each example below is followed by the page it derives from.

The standard HTML tag library is the set of tags in the jakarta.faces.html namespace, conventionally bound to the prefix h. Each tag creates one component in the view’s component tree and attaches the renderer that writes the HTML; the components are explained in the architecture page. The h tags are the vocabulary of every Faces page, and the component libraries covered later in this section (PrimeFaces, OmniFaces) build on them. The f namespace (jakarta.faces.core) tags that appear next to them, such as f:selectItems and f:param, belong to the core tag library, covered on the core tags page; the ones needed here are explained where they are used.

How an HTML Component Tag Works

A tag such as <h:inputText id="reviewer" value="#{reviewBean.reviewer}"/> does four things:

  1. While the view is built, Facelets creates a HtmlInputText component, copies the tag’s attributes into it (as values or as value expressions) and adds it to the parent in the component tree.

  2. While the request is decoded, the component’s renderer reads the request parameter named after the component’s client id and stores it as the component’s submitted value.

  3. The submitted value is converted, validated and, if valid, written to the bean property the value expression points at.

  4. While the response is rendered, the renderer writes the HTML element for the component’s current value.

The tag name therefore selects a component class and a renderer. Tags that look alike can share the behaviour and differ only in the renderer: h:selectOneMenu, h:selectOneListbox and h:selectOneRadio are three tags over the UISelectOne model with three renderers (Menu, Listbox and Radio). The following table is the whole library. The "Component" column names the class in the package jakarta.faces.component.html and the last column gives what the Mojarra 4.1.17 run wrote.

Tag Component HTML it renders

h:form

HtmlForm

<form>, plus a hidden field that tells Faces which form was submitted, and the view state hidden field

h:inputText

HtmlInputText

<input type="text">, or the type named by the type attribute

h:inputSecret

HtmlInputSecret

<input type="password">

h:inputTextarea

HtmlInputTextarea

<textarea>

h:inputHidden

HtmlInputHidden

<input type="hidden">

h:inputFile

HtmlInputFile

<input type="file">

h:selectBooleanCheckbox

HtmlSelectBooleanCheckbox

a single <input type="checkbox">

h:selectOneMenu, h:selectOneListbox

HtmlSelectOneMenu, HtmlSelectOneListbox

<select> with <option> and <optgroup> children

h:selectManyMenu, h:selectManyListbox

HtmlSelectManyMenu, HtmlSelectManyListbox

<select multiple="multiple">

h:selectOneRadio

HtmlSelectOneRadio

<table> (or <ul>) of <input type="radio"> with a <label> each

h:selectManyCheckbox

HtmlSelectManyCheckbox

<table> (or <ul>) of <input type="checkbox"> with a <label> each

h:outputText

HtmlOutputText

the escaped text, wrapped in a <span> only when it has an id, a style or another attribute that needs an element

h:outputFormat

HtmlOutputFormat

text built with java.text.MessageFormat from the value and the f:param children

h:outputLabel

HtmlOutputLabel

<label for="...">

h:outputLink

HtmlOutputLink

<a href="...">, no action and no postback

h:outputScript, h:outputStylesheet

UIOutput (renderers jakarta.faces.resource.Script, jakarta.faces.resource.Stylesheet)

<script src> and <link rel="stylesheet"> for a resource

h:graphicImage

HtmlGraphicImage

<img>

h:commandButton

HtmlCommandButton

<input type="submit">, "reset", "button" or "image"

h:commandLink

HtmlCommandLink

<a href="#" onclick="..."> that submits the form with JavaScript

h:button, h:link

HtmlOutcomeTargetButton, HtmlOutcomeTargetLink

<input type="button"> and <a href> whose target comes from a navigation outcome

h:commandScript

HtmlCommandScript

a <script> that declares a JavaScript function which performs an Ajax request

h:panelGrid

HtmlPanelGrid

<table> with <thead>, <tfoot> and <tbody>

h:panelGroup

HtmlPanelGroup

<div> or <span>, or nothing at all

h:message, h:messages

HtmlMessage, HtmlMessages

<span> and <ul>/<table> of messages

h:dataTable, h:column

HtmlDataTable, HtmlColumn

<table> of data rows (covered on the data tables page)

h:head, h:body, h:doctype

HtmlHead, HtmlBody, HtmlDoctype

<head>, <body> and <!DOCTYPE>

Attributes Shared by Most Tags

Most h tags accept the same families of attributes. The VDL documentation lists them per tag; this is what they mean and where they act in the request.

Attribute Meaning

id

Names the component inside its naming container. Without one, Faces generates an id (j_idt5 on Mojarra, j_id_8 on MyFaces). Give an id to anything that other code refers to: a label, a message, an Ajax render list, a script, a test. The tutorial notes that id is the one attribute that does not take a deferred #{} expression.

rendered

A boolean expression. When it is false the component produces no markup and is skipped when the submitted form is decoded and processed (lifecycle position). It is evaluated on every request, so it must give the same answer on the postback as when the page was rendered.

binding

Stores the component instance in a bean property. See the pitfalls of binding before using it.

value

For output components the value to display; for input components the model value that is read for display and written when the input is valid. A value expression that is writable is required on an input.

converter, converterMessage

Converts between the string in the request and the Java type of the model value (converters page).

required, requiredMessage, validator, validatorMessage

Validation of the submitted value (validators page, Bean Validation).

immediate

Moves conversion, validation and, for a command, the action to the Apply Request Values phase. See the lifecycle page for what it skips.

label

A human name for an input; it replaces the client id at the start of the validation and conversion messages (messages).

disabled, readonly

Rendered as the HTML attributes. On the server, a disabled or read-only input is skipped when decoding, so a request that is forged to send a value for it is ignored (lifecycle position).

style, styleClass, title, lang, dir, role, tabindex, accesskey

Pass-through attributes. styleClass becomes the HTML class. role was added to the pass-through set in 2.2.

onclick, onchange, onfocus, onblur, onkeyup and the other DOM event attributes

Pass-through JavaScript. They are plain browser events, not Faces events; Ajax behaviours are attached with f:ajax (Ajax page).

Any other attribute can be added to an h tag as an HTML5 pass-through attribute with the jakarta.faces.passthrough namespace; this is covered on the HTML5-friendly markup page. Only autocomplete="off" is written by the renderer for the attribute autocomplete: the VDL says any other value renders nothing, and both implementations confirmed it (autocomplete="name" on h:inputText produced no attribute, autocomplete="off" did).

Forms: h:form

Every input and every command component must sit inside an h:form. The form is the unit of submission: only the components in the submitted form are decoded, converted, validated and updated, which is why a page often has several small forms.

<h:form id="review">
  ...
</h:form>

The renderer wrote this for the form above:

<form id="review" name="review" method="post" action="/bookshelf/inputs.xhtml"
      enctype="application/x-www-form-urlencoded">
<input type="hidden" name="review" value="review" />
...
<input type="hidden" name="jakarta.faces.ViewState" id="j_id1:jakarta.faces.ViewState:0"
       value="..." autocomplete="one-time-code" />
</form>

Three things matter in that markup:

  • method is always post; Faces forms are postbacks to the same view (action is the view’s own URL).

  • The hidden field named after the form id tells Faces, while decoding, which form was submitted. MyFaces uses a different convention: it writes <input type="hidden" name="review_SUBMIT" value="1" />. A script that builds a POST by hand has to use the right one.

  • jakarta.faces.ViewState carries the identifier (server-side state saving) or the serialised state (client-side state saving) of the view; without it the postback fails with a ViewExpiredException. With server-side state saving its unguessable value can also act as a defence against cross-site request forgery, under the conditions the security page of this section spells out (this page did not test it). It is written once in every form.

The attributes of h:form: prependId (whether the form’s id is part of the client ids of its children, true by default), enctype (multipart/form-data for file uploads), target, accept, acceptcharset, lang, dir, style, styleClass, title, rendered, binding, role and the DOM events onsubmit and onreset, besides those in the common table.

Rules to remember:

  • Forms cannot be nested. Browsers do not support a form inside a form, and Faces gives no way around it. Use sibling forms.

  • A command outside a form renders but does nothing useful. In project stage Development Mojarra adds an informational message to the page: "The form component needs to have a UIForm in its ancestry. Suggestion: enclose the necessary components within `<h:form>`". MyFaces rendered the button silently.

  • h:inputFile needs enctype="multipart/form-data" on its form. Without it Mojarra rendered a development-stage message ("File upload component requires a form with an enctype of multipart/form-data") and the form stayed application/x-www-form-urlencoded. The file upload page of this section covers the rest.

  • One huge form versus several small ones. Everything in the submitted form is processed, so a validation failure in an unrelated field of the same form blocks the action. Split independent areas, or use f:ajax execute to limit the fields (Ajax page).

The Bookshelf Beans Used by the Examples

All the examples on this page use two small beans of the Bookshelf. ReviewBean holds the form being edited, so it is view scoped (the state lives while the user stays on the view and is serialisable); CatalogueBean serves read-only data to the selection components. The enum and the record are the domain.

package org.example.bookshelf.model;

public enum Genre {
    FICTION("Fiction"), SCIENCE("Science"), HISTORY("History");

    private final String label;

    Genre(String label) {
        this.label = label;
    }

    public String getLabel() {
        return label;
    }
}
package org.example.bookshelf.model;

import java.io.Serializable;

public record Book(long id, String title, String author, Genre genre) implements Serializable {
}
package org.example.bookshelf.web;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.faces.model.SelectItem;
import jakarta.faces.model.SelectItemGroup;
import jakarta.inject.Named;
import java.util.ArrayList;
import java.util.List;
import org.example.bookshelf.model.Book;
import org.example.bookshelf.model.Genre;

@Named
@ApplicationScoped
public class CatalogueBean {

    private final List<Book> books = List.of(
            new Book(1, "Dune", "Frank Herbert", Genre.FICTION),
            new Book(2, "Cosmos", "Carl Sagan", Genre.SCIENCE),
            new Book(3, "SPQR", "Mary Beard", Genre.HISTORY),
            new Book(4, "Solaris", "Stanislaw Lem", Genre.FICTION));

    public List<Book> getBooks() {
        return books;
    }

    public Genre[] getGenres() {
        return Genre.values();
    }

    public List<SelectItem> getFormats() {
        return List.of(new SelectItem("PAPERBACK", "Paperback"),
                new SelectItem("HARDCOVER", "Hardcover"),
                new SelectItem("EBOOK", "E-book", "Digital edition", true));
    }

    public record GenreGroup(String label, List<Book> books) {
    }

    public List<GenreGroup> getGenreGroups() {
        return java.util.Arrays.stream(Genre.values())
                .map(g -> new GenreGroup(g.getLabel(), books.stream().filter(b -> b.genre() == g).toList()))
                .toList();
    }

    public SelectItemGroup[] getGroupedBooks() {
        List<SelectItemGroup> groups = new ArrayList<>();
        for (Genre g : Genre.values()) {
            List<SelectItem> items = new ArrayList<>();
            for (Book b : books) {
                if (b.genre() == g) {
                    items.add(new SelectItem(b.id(), b.title()));
                }
            }
            groups.add(new SelectItemGroup(g.getLabel(), "", false, items.toArray(new SelectItem[0])));
        }
        return groups.toArray(new SelectItemGroup[0]);
    }

    public Book find(long id) {
        return books.stream().filter(b -> b.id() == id).findFirst().orElse(null);
    }
}
package org.example.bookshelf.web;

import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.example.bookshelf.model.Book;
import org.example.bookshelf.model.Genre;

@Named
@ViewScoped
public class ReviewBean implements Serializable {

    private String reviewer;
    private String email;
    private String password;
    private String comment;
    private Integer rating;
    private boolean recommended;
    private String format;
    private Genre genre;
    private Long bookId;
    private Book book;
    private List<String> tags = new ArrayList<>();
    private List<Genre> favouriteGenres = new ArrayList<>();
    private String note = "<b>bold</b> & more";

    public String submit() {
        FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO, "Review saved", "Thanks, " + reviewer));
        return null;
    }

    public String getReviewer() { return reviewer; }
    public void setReviewer(String reviewer) { this.reviewer = reviewer; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public String getComment() { return comment; }
    public void setComment(String comment) { this.comment = comment; }
    public Integer getRating() { return rating; }
    public void setRating(Integer rating) { this.rating = rating; }
    public boolean isRecommended() { return recommended; }
    public void setRecommended(boolean recommended) { this.recommended = recommended; }
    public String getFormat() { return format; }
    public void setFormat(String format) { this.format = format; }
    public Genre getGenre() { return genre; }
    public void setGenre(Genre genre) { this.genre = genre; }
    public Long getBookId() { return bookId; }
    public void setBookId(Long bookId) { this.bookId = bookId; }
    public Book getBook() { return book; }
    public void setBook(Book book) { this.book = book; }
    public List<String> getTags() { return tags; }
    public void setTags(List<String> tags) { this.tags = tags; }
    public List<Genre> getFavouriteGenres() { return favouriteGenres; }
    public void setFavouriteGenres(List<Genre> favouriteGenres) { this.favouriteGenres = favouriteGenres; }
    public String getNote() { return note; }
    public void setNote(String note) { this.note = note; }
}

(The page on scopes and the page on backing beans and CDI explain why @ViewScoped is the right scope for a form bean. For some of the checks below the scratch run used a submit that echoed the converted values in the message detail, so that the values that reached the bean could be read.)

Text Input Components

The four text inputs map to the HTML form controls one to one. They are all UIInput components, so they all share value, converter, required, validator, immediate, valueChangeListener and the message attributes.

  • h:inputText renders <input type="text">. Attributes: maxlength, size, readonly, disabled, alt, autocomplete, accesskey and tabindex, and since 4.0 type. The type attribute (default text) lets the one tag render an HTML5 input such as email, url, number, date or tel without pass-through attributes; the VDL adds that for a type that already has a more specific component (hidden, password, checkbox, radio, file, submit, image, reset, button), the renderer warns in development stage and the more specific component is preferred. The HTML5 page covers the topic.

  • h:inputSecret renders <input type="password">. By default it never writes its value back to the page: the field came back with value="" after a postback that had submitted a password. redisplay="true" changes that and should not be used for a real password, since the value would be in the HTML.

  • h:inputTextarea renders <textarea>; rows and cols size it.

  • h:inputHidden renders <input type="hidden">. The value is written to the page and the browser sends it back, so anyone can change it. Never treat a hidden field as trusted: it is for state that can be re-checked on the server.

  • h:inputFile renders <input type="file"> and submits a jakarta.servlet.http.Part. multiple (to submit a Collection of parts) and accept were added in 4.0. The file upload page of this section covers it.

The Bookshelf review form uses the first three, plus a label and a message beside each:

inputs.xhtml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="jakarta.faces.html"
      xmlns:f="jakarta.faces.core">
<h:head>
  <title>Write a review</title>
</h:head>
<h:body>
  <h:form id="review">
    <h:messages globalOnly="true" showDetail="true" infoClass="ok" errorClass="err"/>
    <h:panelGrid columns="3">
      <h:outputLabel for="reviewer" value="Name"/>
      <h:inputText id="reviewer" value="#{reviewBean.reviewer}"
                   required="true" requiredMessage="Please enter your name"
                   maxlength="40" size="30" autocomplete="name"/>
      <h:message for="reviewer" styleClass="err"/>

      <h:outputLabel for="email" value="E-mail"/>
      <h:inputText id="email" type="email" value="#{reviewBean.email}"/>
      <h:message for="email" styleClass="err"/>

      <h:outputLabel for="password" value="Password"/>
      <h:inputSecret id="password" value="#{reviewBean.password}" redisplay="false"/>
      <h:message for="password" styleClass="err"/>

      <h:outputLabel for="comment" value="Comment"/>
      <h:inputTextarea id="comment" value="#{reviewBean.comment}" rows="4" cols="40"/>
      <h:message for="comment" styleClass="err"/>
    </h:panelGrid>
    <h:inputHidden id="rating" value="#{reviewBean.rating}"/>
    <h:commandButton id="save" value="Save" action="#{reviewBean.submit}"/>
  </h:form>
</h:body>
</html>

Mojarra rendered the body of the form as follows (the <table> is the h:panelGrid, abbreviated):

<table>
<tbody>
<tr>
<td><label for="review:reviewer">Name</label></td>
<td><input id="review:reviewer" type="text" name="review:reviewer" value="" maxlength="40" size="30" /></td>
<td></td>
</tr>
<tr>
<td><label for="review:email">E-mail</label></td>
<td><input id="review:email" type="email" name="review:email" value="" /></td>
<td></td>
</tr>
<tr>
<td><label for="review:password">Password</label></td>
<td><input id="review:password" type="password" name="review:password" value="" /></td>
<td></td>
</tr>
<tr>
<td><label for="review:comment">Comment</label></td>
<td><textarea id="review:comment" name="review:comment" cols="40" rows="4"></textarea></td>
<td></td>
</tr>
</tbody>
</table>
<input id="review:rating" type="hidden" name="review:rating" value="" />
<input id="review:save" type="submit" name="review:save" value="Save" />

Points the output makes visible:

  • The for of each label became the client id of its input (review:reviewer), resolved from the plain id the page used. That is what makes clicking the label focus the field, and what screen readers use.

  • autocomplete="name" did not appear on the first input.

  • The empty h:message cells are <td></td>: with no message to show and no id, h:message wrote nothing at all.

What happens on a postback:

  1. Apply Request Values. Each input’s renderer reads review:reviewer and the others from the request. An input whose parameter is absent from the request is left alone (the run posted a form without the field and required did not fire); one that arrives empty stores "".

  2. Process Validations. An empty reviewer fails required and the page redisplays with the message "Please enter your name". Without requiredMessage the text is the standard "Validation Error: Value is required.", prefixed by the label or, when there is no label, by the client id.

  3. Update Model Values. Valid values are written to the bean. The run posted a name and an empty comment and the bean received comment='', an empty string, not null. Setting the context parameter jakarta.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL to true in web.xml makes the same post leave the property null (confirmed on Mojarra). According to the specification text, the standard converters already return null for an empty string (read, not run here).

  4. Invoke Application. save runs submit().

Selection Components

Selection components let the user pick from a list of options. There are seven tags, and they differ in whether they accept one value or many, and in how the options are drawn.

Tag Selects Bean property type Looks like

h:selectBooleanCheckbox

one yes/no

boolean or Boolean

a single checkbox

h:selectOneMenu

one of many

the type of the item values

a drop-down

h:selectOneListbox

one of many

the type of the item values

a list box (size rows)

h:selectOneRadio

one of many

the type of the item values

radio buttons

h:selectManyMenu

many

array or Collection of the item type

a multi-select drop-down

h:selectManyListbox

many

array or Collection of the item type

a multi-select list box (size rows)

h:selectManyCheckbox

many

array or Collection of the item type

a group of checkboxes

The options come from child tags, f:selectItem for one and f:selectItems for a collection, both from the core tag library.

Defining the options

<h:selectOneMenu id="format" value="#{reviewBean.format}">
  <f:selectItem itemLabel="-- choose --" itemValue="" noSelectionOption="true"/>
  <f:selectItems value="#{catalogueBean.formats}"/>
</h:selectOneMenu>

f:selectItem takes itemValue (what is submitted and converted), itemLabel (what the user sees), itemDescription, itemDisabled, itemEscaped (default true) and noSelectionOption. f:selectItems takes a value that is an array, a Collection or a Map:

  • a collection or array of jakarta.faces.model.SelectItem objects (the form used for getFormats() above), or of SelectItemGroup objects (see Grouped options: f:selectItemGroup and f:selectItemGroups);

  • a collection or array of any objects, in which case var, itemValue and itemLabel say how to read each one:

    <f:selectItems value="#{catalogueBean.books}" var="b" itemValue="#{b.id}"
                   itemLabel="#{b.title} (#{b.author})"/>

    itemDisabled, itemDescription and itemLabelEscaped take expressions on var too, and noSelectionValue marks the element that stands for "no selection";

  • a Map, whose keys are the labels and whose values are the item values. A LinkedHashMap keeps the order; the run rendered Paperback and Hardcover as labels over the values PAPERBACK and HARDCOVER.

An enum needs no converter: the run submitted FICTION and the property of type Genre received Genre.FICTION, because Faces applies the enum converter implicitly. Long and the other wrapper types also convert implicitly. Any other class needs a converter, as shown in Selecting objects, and the "Value is not valid" error.

The itemDisabled item is drawn but cannot be chosen: new SelectItem("EBOOK", "E-book", "Digital edition", true) rendered <option value="EBOOK" disabled="disabled">E-book</option>. The description was not written to the HTML.

The no-selection option

A menu shows its first option when the value is empty, so a prompt such as "-- choose --" has to be an item with an empty value. Marking it noSelectionOption="true" (on f:selectItem) or noSelectionValue (on f:selectItems) tells Faces the item stands for "nothing", and with required="true" a user who leaves it selected fails validation. The run confirmed it: a required menu left on its prompt came back with "Edition: Validation Error: Value is required." (the text before the colon is the label).

<h:selectOneMenu id="edition" label="Edition" required="true" value="#{reviewBean.format}">
  <f:selectItem itemLabel="-- choose --" itemValue="" noSelectionOption="true"/>
  <f:selectItems value="#{catalogueBean.formats}"/>
</h:selectOneMenu>

hideNoSelectionOption="true" removes the prompt once a value is selected. On Mojarra, after a post with HARDCOVER, the menu came back with the three formats and without the "-- choose --" option. This was not checked on MyFaces.

Rendering

The same options render differently per tag. All seven in one form, as run:

selects.xhtml (excerpt)
<h:form id="pick">
  <h:selectBooleanCheckbox id="recommended" value="#{reviewBean.recommended}"/>
  <h:outputLabel for="recommended" value="I recommend this book"/>

  <h:selectOneRadio id="genre" value="#{reviewBean.genre}" layout="pageDirection">
    <f:selectItems value="#{catalogueBean.genres}" var="g" itemValue="#{g}" itemLabel="#{g.label}"/>
  </h:selectOneRadio>

  <h:selectOneListbox id="book" size="4" value="#{reviewBean.bookId}">
    <f:selectItems value="#{catalogueBean.books}" var="b" itemValue="#{b.id}"
                   itemLabel="#{b.title} (#{b.author})"/>
  </h:selectOneListbox>

  <h:selectManyCheckbox id="tags" value="#{reviewBean.tags}" layout="lineDirection">
    <f:selectItem itemValue="classic" itemLabel="Classic"/>
    <f:selectItem itemValue="short" itemLabel="Short read"/>
  </h:selectManyCheckbox>

  <h:selectManyListbox id="favs" size="3" value="#{reviewBean.favouriteGenres}">
    <f:selectItems value="#{catalogueBean.genres}" var="g" itemValue="#{g}" itemLabel="#{g.label}"/>
  </h:selectManyListbox>

  <h:selectManyMenu id="favmenu" value="#{reviewBean.favouriteGenres}">
    <f:selectItems value="#{catalogueBean.genres}" var="g" itemValue="#{g}" itemLabel="#{g.label}"/>
  </h:selectManyMenu>
</h:form>
<input id="pick:recommended" type="checkbox" name="pick:recommended" />
<label for="pick:recommended">I recommend this book</label>

<table border="0" id="pick:genre">
  <tr><td>
    <input type="radio" name="pick:genre" id="pick:genre:0" value="FICTION" />
    <label for="pick:genre:0"> Fiction</label></td></tr>
  <tr><td>
    <input type="radio" name="pick:genre" id="pick:genre:1" value="SCIENCE" />
    <label for="pick:genre:1"> Science</label></td></tr>
  ...
</table>

<select id="pick:book" name="pick:book" size="4">
  <option value="1">Dune (Frank Herbert)</option>
  <option value="2">Cosmos (Carl Sagan)</option>
  ...
</select>

<select id="pick:favs" name="pick:favs" multiple="multiple" size="3">
  <option value="FICTION">Fiction</option>
  ...
</select>
<select id="pick:favmenu" name="pick:favmenu" multiple="multiple" size="1">
  ...
</select>

(The listing is re-indented for reading. The h:selectManyCheckbox markup has the shape of the radio table, with <input type="checkbox"> items on a single row for lineDirection.)

How the pieces are named:

  • The <select> and the <table> carry the component’s client id; each radio and checkbox gets that id with :0, :1, …​ appended, which is also what each <label for> points at.

  • h:selectBooleanCheckbox has no label of its own: pair it with an h:outputLabel. When the checkbox is unchecked the browser sends nothing, and the renderer treats a missing parameter as false, so the property is correctly reset (the specification defines this in the decode behaviour).

  • h:selectManyMenu is a <select multiple> that Mojarra rendered with size="1"; unlike the list box it has no size attribute, so the browser decides how to draw it.

  • h:selectManyCheckbox and h:selectManyListbox take a collectionType naming the Collection class to create when the property is not an array or already initialised (collectionType="java.util.TreeSet" produced a TreeSet).

Layout of radio buttons and checkboxes

h:selectOneRadio and h:selectManyCheckbox accept layout:

layout Markup

lineDirection (default)

one <table> row, items side by side

pageDirection

one <table> with one row per item, items stacked

list (4.0)

a <ul> with one <li> per item, no table. border is ignored. This is the layout to use when a table is the wrong semantic or the CSS cannot style it.

<h:selectOneRadio id="listradio" value="#{reviewBean.genre}" layout="list">
  <f:selectItems value="#{catalogueBean.genres}" var="g" itemValue="#{g}" itemLabel="#{g.label}"/>
</h:selectOneRadio>
<h:selectManyCheckbox id="listcheck" value="#{reviewBean.favouriteGenres}" layout="list" styleClass="opts">
  <f:selectItems value="#{catalogueBean.genres}" var="g" itemValue="#{g}" itemLabel="#{g.label}"/>
</h:selectManyCheckbox>
<ul id="pick:listradio">
<li>
<input type="radio" name="pick:listradio" id="pick:listradio:0" value="FICTION" /><label for="pick:listradio:0"> Fiction</label></li>
<li>
...
</ul>
<ul id="pick:listcheck" class="opts">
<li>
<input name="pick:listcheck" id="pick:listcheck:0" value="FICTION" type="checkbox" /><label for="pick:listcheck:0" class=""> Fiction</label></li>
...
</ul>

The styleClass moved to the <ul>, and list worked the same on MyFaces. The VDL also lists enabledClass and disabledClass for the labels of both tags, and selectedClass and unselectedClass for the checkboxes.

Radio buttons that must sit in different places of the page can use the group attribute of h:selectOneRadio (Faces 2.3). Each tag then renders only the radios of its own items, and all of them share the name of the group:

<h:selectOneRadio id="a" group="genre" value="#{reviewBean.genre}">
  <f:selectItem itemValue="FICTION" itemLabel="Fiction"/>
</h:selectOneRadio>
<h:selectOneRadio id="b" group="genre" value="#{reviewBean.genre}">
  <f:selectItem itemValue="SCIENCE" itemLabel="Science"/>
</h:selectOneRadio>

Mojarra rendered <input type="radio" name="r:genre" id="r:a" value="r:a:FICTION" /> and a second radio with id="r:b" and value="r:b:SCIENCE", and a post of r:b:SCIENCE set the property to SCIENCE. The group attribute is described in the rendering text of the VDL for h:selectOneRadio and in the tutorial, although it is missing from that tag’s attribute table. On MyFaces 4.1.4 the same markup rendered correctly for a String property, but with an enum-typed property the page failed with "r:a: 'FICTION' must be convertible to an enum."; this is a MyFaces behaviour to check before relying on group with enums.

Grouped options: f:selectItemGroup and f:selectItemGroups

Menus and list boxes can draw <optgroup> headings. There are two ways to describe the groups.

From Java, return SelectItemGroup objects (a label plus its SelectItem children) in the collection given to f:selectItems; getGroupedBooks() above does that:

<h:selectOneMenu id="grouped" value="#{reviewBean.bookId}">
  <f:selectItems value="#{catalogueBean.groupedBooks}"/>
</h:selectOneMenu>

From the view, f:selectItemGroup (a single group, 4.0) and f:selectItemGroups (a group for every element of a collection, 4.0) nest f:selectItems children. f:selectItemGroups takes value (array or Iterable), var (the current group, visible to the nested tags) and itemLabel and itemDisabled. The nested f:selectItems must read the items of that group from var; pointing it at the whole book list would repeat every book in every group (the run printed four books under each heading until value was changed to g.books):

<h:selectOneMenu id="grouped2" value="#{reviewBean.bookId}">
  <f:selectItemGroups value="#{catalogueBean.genreGroups}" var="g" itemLabel="#{g.label}">
    <f:selectItems value="#{g.books}" var="b" itemValue="#{b.id}" itemLabel="#{b.title}"/>
  </f:selectItemGroups>
</h:selectOneMenu>

Both forms rendered the same HTML:

<select id="pick:grouped2" name="pick:grouped2" size="1">
<optgroup label="Fiction">
  <option value="1">Dune</option>
  <option value="4">Solaris</option>
</optgroup>
<optgroup label="Science">
  <option value="2">Cosmos</option>
</optgroup>
<optgroup label="History">
  <option value="3">SPQR</option>
</optgroup>
</select>

Radio and checkbox renderers draw a group as a nested root element (a nested table or list) instead.

Selecting objects, and the "Value is not valid" error

Faces compares what the browser sent with the options that were rendered. Two errors follow from that.

The submitted value must be one of the options. A value that is not in the list (a forged request, or a list that changed between render and postback) fails with "pick:book: Validation Error: Value is not valid" (the run posted a book id that was not offered). Rebuild the list identically on the postback: a @RequestScoped or @ViewScoped bean that recomputes the options must produce the same set, and a list that depends on request state must be rebuilt from state that survives (view scope).

Item values that are not simple types need a converter. The Bookshelf menu below uses the Book objects themselves as item values, with no converter:

<h:selectOneMenu id="objmenu" value="#{reviewBean.book}">
  <f:selectItems value="#{catalogueBean.books}" var="b" itemValue="#{b}" itemLabel="#{b.title}"/>
</h:selectOneMenu>

The renderer used toString() for the option values (Book[id=1, title=Dune, ...]) and the postback failed with "Conversion Error setting value 'Book[id=1, …​]' for 'null Converter'.". A converter that maps the book to its id and back repairs it. A managed converter, registered for the class and able to inject the catalogue, is the shortest form (converters page):

@FacesConverter(forClass = Book.class, managed = true)
@ApplicationScoped
public class BookConverter implements Converter<Book> {

    @Inject
    private CatalogueBean catalogue;

    @Override
    public Book getAsObject(FacesContext context, UIComponent component, String value) {
        if (value == null || value.isBlank()) {
            return null;
        }
        try {
            return catalogue.find(Long.parseLong(value));
        } catch (NumberFormatException e) {
            throw new ConverterException(e);
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Book value) {
        return value == null ? "" : String.valueOf(value.id());
    }
}

With it the same menu rendered <option value="1">Dune</option> and a post of 3 set the property to the SPQR book. The alternative that needs no converter is to submit the id (itemValue="#{b.id}", a Long) and look the book up in the action, as the list box above does.

Labels are escaped. itemLabel and itemLabelEscaped (and itemEscaped on f:selectItem) default to true. Setting them to false made the label <b>bold</b> render as real bold markup in the run, which is only safe for labels written by the page author and never for text a user can influence.

Output Components

h:outputText

Writes the value as text. The text is escaped by default: with note = "<b>bold</b> & more",

<h:outputText id="n1" value="#{reviewBean.note}"/>
<h:outputText id="n2" value="#{reviewBean.note}" escape="false"/>
<h:outputText id="n3" value="#{reviewBean.note}" styleClass="note" title="a note"/>
<span id="n1">&lt;b&gt;bold&lt;/b&gt; &amp; more</span>
<span id="n2"><b>bold</b> & more</span>
<span id="n3" class="note" title="a note">&lt;b&gt;bold&lt;/b&gt; &amp; more</span>

The <span> exists only when the tag has an id, a style or another attribute that needs an element; without any of them the renderer writes just the text (the run printed no id for <h:outputText value="no id"/>). escape="false" writes the value as markup. It must never be used for text that comes from a user, a database field they can edit or a request parameter, because that is cross-site scripting; escape the value or render it with the default. The same goes for h:outputLabel escape and for itemLabelEscaped. Expressions written in template text are escaped the same way (see the escaping section of the Facelets page).

h:outputText also takes converter, so <h:outputText value="#{book.published}"><f:convertDateTime/></h:outputText> formats a date (converters page).

h:outputFormat

Builds a message with java.text.MessageFormat, with parameters supplied by f:param children in order:

<h:outputFormat id="fmt" value="{0} has {1} reviews">
  <f:param value="Dune"/>
  <f:param value="42"/>
</h:outputFormat>

which produced <span id="fmt">Dune has 42 reviews</span>. The value is usually a resource bundle message such as #{msg['book.reviews']}, so the sentence is translated while the parameters stay in the view (messages and internationalisation page).

h:outputLabel

Renders <label for="..."> and is the accessible way to name an input. for takes the id of the input as written in the page and Faces resolves it to the client id, as the review form showed. The text comes from value or from child content, and escape defaults to true. Always give inputs a label: the label increases the click target, and assistive technology reads it; see Accessibility and Forms: Accessibility & Validation for the rules.

If for names no component, the behaviour differs: in the scratch run an h:outputLabel outside any form rendered a bare <label> on Mojarra and <label for=""> on MyFaces. Check the rendered HTML when labels do not focus their input.

Writes <a href="..."> from value, with f:param children added as encoded query parameters. It is a link only: no action, no Faces navigation and no postback, so it suits external URLs.

<h:outputLink id="docs" value="https://jakarta.ee/specifications/faces/4.1/">Spec</h:outputLink>
<h:outputLink value="/bookshelf/inputs.xhtml"><f:param name="a" value="b c"/>Link text</h:outputLink>

The second rendered <a href="/bookshelf/inputs.xhtml?a=b+c">Link text</a>: the value b c was URL-encoded. For links inside the application that should follow navigation rules and view parameters, use h:link (Buttons and Links).

h:graphicImage

Renders <img>. There are two ways to give the source:

  • name and library refer to a resource: a file under resources/ (here resources/img/cover.png), served through the Faces resource handler with caching and versioning. <h:graphicImage name="cover.png" library="img"/> rendered src="/bookshelf/jakarta.faces.resource/cover.png.xhtml?ln=img" on both implementations.

  • url (or its alias value) is a context-relative URL. <h:graphicImage url="/images/cover.png"/> rendered src="/bookshelf/images/cover.png": a path starting with / is prefixed with the context path.

Always give alt (empty for a purely decorative image). width, height, longdesc, usemap and ismap pass through. The resource handling page of this section explains libraries, versions and locales.

h:outputScript, h:outputStylesheet, h:head and h:body

h:outputStylesheet name="app.css" library="css" rendered <link rel="stylesheet" href="...?ln=css" /> inside the page head; h:outputScript renders a <script src> and accepts target="head" or target="body". A script that is written twice with the same name and library was rendered once. These two tags need h:head and h:body rather than the plain HTML elements, because the renderers of h:head and h:body are where resources targeted at the head and the body are written (the Facelets page explains it in the h:head and h:body section). Faces also needs its faces.js library for Ajax and for h:commandLink; Mojarra wrote the <script> for it inline, just before the first command link of the form.

h:doctype renders a document type declaration at the position it is written, for views that do not use an HTML5 <!DOCTYPE html>. <h:doctype rootElement="html" public="-//W3C//DTD HTML 4.01//EN" system="http://www.w3.org/TR/html4/strict.dtd"/> rendered the matching declaration in the run; rootElement is required (without it Mojarra wrote <!DOCTYPE null> and MyFaces <!DOCTYPE >). Ordinary Facelets pages simply start with <!DOCTYPE html>, as in the examples above.

Since Faces 4.1 the VDL lists an id attribute on h:head and h:body (the component identifier, unique within the closest naming container). The documentation adds that the attribute is rendered only when the current doctype is an HTML5 doctype, so <h:body id="page"> yields <body id="page"> on a <!DOCTYPE html> page and no id on an older doctype. This rendering rule is quoted from the VDL; it was not run on either implementation.

Buttons and Links

There are six ways to trigger something from a Faces page. They differ in whether they submit the form, whether they use POST or GET, and what the target is.

Tag Method Needs a form Use it for

h:commandButton

POST (postback)

yes

submit, reset or plain button; runs an action

h:commandLink

POST (postback, via JavaScript)

yes

an action that looks like a link, such as "Delete" in a table row

h:button

GET (JavaScript sets window.location)

no

navigation to another view without side effects

h:link

GET (plain a href)

no

the same, as a real link that can be bookmarked and opened in a new tab

h:outputLink

GET

no

a URL that is not a Faces view

h:commandScript

Ajax POST

yes

a JavaScript function that calls the server

commands.xhtml
<h:form id="nav">
  <h:commandButton id="save" value="Save" action="#{reviewBean.submit}"/>
  <h:commandButton id="reset" type="reset" value="Reset"/>
  <h:commandLink id="more" value="Save and stay" action="#{reviewBean.submit}">
    <f:param name="from" value="link"/>
  </h:commandLink>
  <h:button id="home" value="Home" outcome="inputs"/>
  <h:link id="sel" value="Selects" outcome="selects" fragment="top">
    <f:param name="q" value="dune"/>
  </h:link>
  <h:outputLink id="docs" value="https://jakarta.ee/specifications/faces/4.1/">Spec</h:outputLink>
</h:form>

Mojarra’s output:

<input id="nav:save" type="submit" name="nav:save" value="Save" />
<input id="nav:reset" type="reset" name="nav:reset" value="Reset" />
<script src="/bookshelf/jakarta.faces.resource/faces.js.xhtml?ln=jakarta.faces&amp;v=4.1.17&amp;stage=Development"></script>
<a id="nav:more" href="#"
   onclick="mojarra.cljs(document.getElementById('nav'),{'nav:more':'nav:more','from':'link'},'');event.preventDefault()">Save and stay</a>
<input id="nav:home" type="button"
       onclick="window.location.href='/bookshelf/inputs.xhtml'; return false;" value="Home">
<a id="nav:sel" name="nav:sel" href="/bookshelf/selects.xhtml?q=dune#top">Selects</a>
<a id="nav:docs" name="nav:docs" href="https://jakarta.ee/specifications/faces/4.1/">Spec</a>

Both fire an ActionEvent when activated. action is either a method expression whose result is the navigation outcome (#{reviewBean.submit} returned null, so the same view was redisplayed) or a literal outcome string. actionListener runs before the action and cannot change the navigation; immediate="true" moves both to the Apply Request Values phase and skips conversion and validation of the other inputs, which is right for a "Cancel" button and wrong almost everywhere else (lifecycle page).

h:commandButton takes type (submit by default, button, reset) and image. With image the element is <input type="image"> and the value is not shown: image="/images/cover.png" rendered type="image" and src="/bookshelf/images/cover.png". type="reset" clears the browser form without contacting the server, as does a type="button" with its own onclick; neither triggers an action. The specification says a reset is ignored on decode.

h:commandLink has no HTML equivalent of its own: it renders an anchor whose onclick submits the surrounding form with JavaScript that Faces provides (mojarra.cljs on Mojarra, myfaces.oam.submitForm on MyFaces) and adds faces.js. It therefore requires JavaScript and does not work in a new tab, and f:param children become extra request parameters of the post (from=link above). Prefer a button for destructive actions and a link for navigation.

These two are the outcome-target components. They have no action; they use outcome and the navigation rules to compute a URL at render time, and the user’s click is a plain GET. Attributes: outcome, includeViewParams (add the current view parameters to the URL), fragment (the #anchor), f:param children (query parameters), disabled, disableClientWindow, and for h:button an image, for h:link the anchor attributes such as rel and target. The page on navigation covers outcomes and the page on view parameters covers includeViewParams.

Because the URL exists in the HTML, h:link supports "open in new tab", bookmarking and crawling, and it needs no postback or view state. When the outcome cannot be resolved Faces disables the control rather than render a broken URL: <h:link outcome="nonexistent"/> became a <span> with the text "gone: This link is disabled because a navigation case could not be matched." and an h:button was rendered disabled, with the same message, and a development-stage message "Unable to find matching navigation case from view ID '/more.xhtml' for outcome 'nonexistent'" was added. (These messages appear in Development stage only.)

A GET must not change state. A link such as ?delete=42 that deletes on request is exposed to prefetching, crawlers and cross-site request forgery; changes belong to a POST command component with view state.

h:commandScript

h:commandScript (2.3) declares a JavaScript function that, when called, sends an Ajax request (execute, render, resetValues, onevent, onerror and action work as on f:ajax). name is required; autorun="true" calls the function when the page loads.

<h:form id="up">
  <h:commandScript id="cs" name="refreshFeed" execute="@form" render="out" action="#{reviewBean.submit}"/>
  <h:outputText id="out" value="x"/>
</h:form>

The renderer wrote <span id="up:cs"><script>var refreshFeed=function(o){...mojarra.ab('up:cs',null,'action','@form','up:out',{'params':o})}</script></span>: the identifier render="out" was resolved to the client id up:out, and calling refreshFeed({reviewer: 'x'}) from any script passes the object properties as request parameters. This is the tool to trigger Faces from non-Faces JavaScript such as a timer, a WebSocket handler or a third-party widget; the Ajax page of this section covers the details, and WebSocket push shows the server-initiated case.

Panels: h:panelGrid and h:panelGroup

h:panelGrid lays its children out in an HTML table with columns cells per row. The header, footer and caption facets become <thead>, <tfoot> and <caption>; rowClasses and columnClasses (comma-separated lists, cycled) style rows and cells; bodyrows starts a new <tbody> at the given row indices.

<h:panelGrid id="grid" columns="2" border="1" columnClasses="k,v" rowClasses="odd,even">
  <f:facet name="header">Details</f:facet>
  <h:outputText value="Title"/><h:outputText value="Dune"/>
  <h:outputText value="Author"/><h:outputText value="Frank Herbert"/>
  <f:facet name="footer">end</f:facet>
</h:panelGrid>
<table id="grid" border="1">
<thead>
<tr><th colspan="2" scope="colgroup">Details</th></tr>
</thead>
<tfoot>
<tr><td colspan="2">end</td></tr>
</tfoot>
<tbody>
<tr class="odd">
<td class="k">Title</td>
<td class="v">Dune</td>
</tr>
<tr class="even">
<td class="k">Author</td>
<td class="v">Frank Herbert</td>
</tr>
</tbody>
</table>

The footer comes before the body in the markup, as the HTML table model requires. Two cautions:

  • h:panelGrid is for tabular arrangements, such as a label/input form. Whole-page layout belongs to CSS (grid or flexbox); a panelGrid of panelGrid`s produces nested tables that are hard to style and to read with a screen reader. `h:panelGrid cannot span cells: every child occupies exactly one cell, so a component that needs a wider cell must be wrapped, and several components that must share one cell go in an h:panelGroup.

  • Count the children. A columns="2" grid fills two cells per row from the children that are rendered, so a child with rendered="false" does not leave a gap: with children A, B (not rendered), C and D the run put A and C on the first row and D alone on the second, which shifts every following cell. An h:message that has nothing to show still occupies its cell (<td></td>), which is why the review form keeps its columns aligned. MyFaces padded the short last row with an empty <td>; Mojarra did not.

h:panelGroup groups components so that they count as one child, and optionally renders a container. With layout="block" it is a <div>, otherwise a <span>, and in both cases only when it has an id, a style or a class. In the run <h:panelGroup id="blk" layout="block" styleClass="box">inside</h:panelGroup> gave <div id="blk" class="box">inside</div>, a group with an id and no layout gave <span id="inl">inline</span>, and a group with neither an id nor a style produced no element, only its children. It is also the standard way to give an Ajax render target to a block of components that may or may not be rendered: if the container is conditional, there is no element in the page to update.

h:dataTable and h:column produce a table over a collection and are covered on the data tables and iteration page of this section.

Messages: h:message and h:messages

Faces collects messages (jakarta.faces.application.FacesMessage) during the request, from converters, validators, Bean Validation, the framework and from your code. Two components display them.

h:message h:messages

Shows

the messages for one component (for is required)

all the messages, or those of one component (for), or only global ones (globalOnly="true")

Default text shown

the detail (showDetail defaults to true, showSummary to false)

the summary (showSummary defaults to true, showDetail to false)

Layout

one message

layout="list" (default, <ul>) or table

Both take errorClass, warnClass, infoClass, fatalClass and the matching *Style, tooltip (show the detail as a title), and styleClass, style, title, lang and dir; h:message also takes role. A message added with a client id is "component-associated"; one added with null is global. The review bean’s action added a global message and the form showed it with <h:messages globalOnly="true" showDetail="true"/>:

FacesContext.getCurrentInstance().addMessage(null,
        new FacesMessage(FacesMessage.SEVERITY_INFO, "Review saved", "Thanks, " + reviewer));
<ul><li class="ok">	Review saved Thanks, Ann</li></ul>

(The list item had both the summary and the detail because the tag asked for both.) Validation and conversion failures are associated with their component, so a per-field h:message shows them. The text of a failure starts with the component’s label or, when it has none, its client id:

<span id="f:m" class="err" title="f:name: Validation Error: Value is required.">	f:name: Validation Error: Value is required.</span>

Give every input a label attribute to get "Edition: Validation Error: Value is required." rather than "pick:reqmenu: …​". Replace the standard texts with requiredMessage, converterMessage and validatorMessage on the input, or per language with a message bundle (messages and internationalisation page).

Two behaviours help with Ajax:

  • A message component with no messages still renders its element when it has an id: h:messages id="msgs" wrote <div id="f:msgs"></div> (or a <table>, with layout="table") and h:message id="m" wrote <span id="f:m"></span>. That empty container is what an f:ajax render="msgs" replaces, and it is why a message component needs an id to take part in partial rendering. A message tag with no id and no content wrote nothing.

  • In project stage Development, a message that no h:message or h:messages displayed is not lost: Mojarra added a <ul id="jakarta_faces_developmentstage_messages"> to the end of the page, MyFaces a <div> with the same id. It is how you find out that a validation error was raised for a component that has no message tag.

Rendered HTML and Client IDs

The id you write is only the last segment of the id that reaches the browser. The client id is built from the ids of all the naming containers above the component, joined with the separator character, which is : by default. h:form, h:dataTable, ui:repeat and composite components are naming containers (the architecture page explains how client ids are built). The scratch run printed these cases (Mojarra):

<h:form id="search">
  <h:inputText id="q" value="#{reviewBean.reviewer}" onfocus="console.log('#{component.clientId}')"/>
  <h:inputText value="#{reviewBean.email}"/>
  <h:panelGroup id="g"><h:inputText id="inner"/></h:panelGroup>
</h:form>
<h:form id="noprefix" prependId="false">
  <h:inputText id="q2"/>
</h:form>
<h:form>
  <h:inputText id="q3"/>
</h:form>
<h:form id="rep">
  <ui:repeat value="#{catalogueBean.books}" var="b" varStatus="s">
    <h:inputText id="t" value="#{b.title}"/>
  </ui:repeat>
</h:form>
<h:form id="dt">
  <h:dataTable id="tbl" value="#{catalogueBean.books}" var="b">
    <h:column><h:outputText id="title" value="#{b.title}"/></h:column>
  </h:dataTable>
</h:form>
<input id="search:q" type="text" name="search:q" value="" onfocus="console.log('search:q')" />
<input type="text" name="search:j_idt5" value="" />
<span id="search:g"><input id="search:inner" type="text" name="search:inner" value="" /></span>

<input id="q2" type="text" name="q2" value="" />

<form id="j_idt6" ...><input id="j_idt6:q3" type="text" name="j_idt6:q3" value="" />

<input id="rep:j_idt7:0:t" type="text" name="rep:j_idt7:0:t" value="Dune" />
<input id="rep:j_idt7:1:t" ... value="Cosmos" />

<span id="dt:tbl:0:title">Dune</span>
<span id="dt:tbl:1:title">Cosmos</span>

What it shows:

  • A component without an id gets a generated one, j_idt5. Mojarra used it only as the name of the input and did not write an HTML id for it; MyFaces wrote id="search:j_id_8" too. It is not stable: adding a component earlier in the page renumbers the later ones. In the scratch app the submit button was pick:j_idt21 and became pick:j_idt25 after two menus with their f:selectItem and f:selectItems children were added above it, so a test or script that hard-coded the generated id broke. Any component you refer to must have an explicit id. MyFaces generates j_id_8 instead.

  • prependId="false" removes the form’s id from its children (q2). The risk is duplicates: two forms with the same child id then collide. Do not use it with several forms on a page.

  • Inside a repeating component the client id includes the row index (dt:tbl:0:title), and a ui:repeat adds its own generated id segment (rep:j_idt7:0:t) when it has no id; give it one.

  • The name attribute of an input is its client id, which is what the decode reads.

  • #{component.clientId} is available in the attribute of a tag and gives the id of the component the attribute belongs to, the stable way to hand an id to JavaScript.

The separator is configurable. The context parameter jakarta.faces.SEPARATOR_CHAR set to - made the same page render search-q and search-g, which avoids escaping in CSS selectors (the colon of a client id must be written #search\:q in a CSS selector, while document.getElementById('search:q') needs no escaping). The parameter is global, so everything that builds ids by hand or tests that match them must agree; a component library can break when the separator changes. The default is the safe choice.

Rules of thumb:

  • Reference components by search expression (@form, @this, absolute ids starting with :, relative ids) from render and execute, and avoid string-concatenating client ids in Java. The page on search expressions describes them.

  • Use CSS classes, not #id selectors, for styling.

  • In tests, give the elements data-testid pass-through attributes (HTML5 page) or `id`s, and match with a stable selector instead of the generated id.

Where the Components Act in the Request Processing Lifecycle

Phase What the standard HTML components do

Restore View

On the first request the component tree is built from the Facelets view; on a postback it is rebuilt and the view state is restored. Every tag of this page becomes a component here.

Apply Request Values

Every component that is rendered, enabled and not read-only calls decode. An input reads the parameter named after its client id and stores the string as its submitted value. A command button looks for its client id (or .x and .y for an image) and queues an ActionEvent. h:selectBooleanCheckbox turns a missing parameter into false. An immediate input or command acts here.

Process Validations

The submitted strings are converted, then validated: required, the validators, and for the select components the check that the value is one of the offered options. Messages are added here, and an error moves the request straight to Render Response.

Update Model Values

Valid values are written to the beans.

Invoke Application

Action listeners and actions of h:commandButton, h:commandLink and h:commandScript run; the outcome picks the next view.

Render Response

Renderers encode the HTML; h:message shows the messages; for h:button and h:link the target URL is computed.

Two server-side rules follow from this and were checked on both implementations. A request that sends a value for a component that is disabled, readonly or has rendered="false" is ignored for that component: the bean kept its old value when the run posted outer:a=hacked, outer:b=hacked and outer:c=hacked for a disabled, a read-only and a non-rendered input. This protects the data that the page hides or locks, but only as far as the attribute is not itself computed from forgeable state; and it is not authorisation: a user who can reach the action can call it, whatever the page shows. Check permissions in the bean or with Jakarta Security (security page), not with rendered.

See the lifecycle page for the six phases in full, Apply Request Values and the immediate attribute.

Mojarra Versus MyFaces

Both implementations pass the Jakarta Faces compatibility tests, so the components behave the same. What differs is markup details and defaults, which matters when you test with strings, write CSS that depends on the structure, or post forms with scripts. Differences seen in the scratch run (Mojarra 4.1.17, MyFaces 4.1.4):

Area Mojarra MyFaces

Hidden field identifying the submitted form

<input type="hidden" name="review" value="review" />

<input type="hidden" name="review_SUBMIT" value="1" />

Generated ids

j_idt5, j_idt6; written as the name of an input but not as its HTML id

j_id_8, j_id_d; also written as the HTML id

Whitespace in output

newlines and tabs inside tables and selects

compact, mostly single-line markup

Checkbox

<input type="checkbox" name="..." />

adds value="true"

Labels in h:selectManyCheckbox

a space before the label text, class="" on the label

the entity   (non-breaking space) before the label text

h:commandLink script

mojarra.cljs(...)

myfaces.oam.submitForm(...)

h:dataTable body

<tbody>

<tbody id="dt:tbl:tbody_element">

No-selection prompt of a menu whose value is null

the prompt option is rendered, not marked

the prompt option has selected="selected"

h:panelGrid whose last row is short

the row has fewer cells

the missing cell is padded with an empty <td>

h:outputLabel whose for names no component

<label> without for

<label for="">

h:doctype without attributes

<!DOCTYPE null>

<!DOCTYPE >

Command outside a form

development-stage message about the missing UIForm

no message in the run

h:selectOneRadio group with an enum-typed property

works

failed to render in 4.1.4 (String property worked)

The markup differences are harmless to pages written to the specification: use ids and classes you set yourself, not the generated structure, and never compare rendered HTML in a test. The versions page compares the implementations in general.

Common Pitfalls

  • Inputs or commands outside an h:form. They render but do not submit. Mojarra’s development-stage message names the cause; MyFaces may say nothing.

  • Nested forms. Not supported by HTML; use sibling forms.

  • A required field that is skipped by the button you want. The button validates the whole form. Put the "Cancel" button in its own form, or in the same form with immediate="true" only after reading the lifecycle page.

  • Missing label. Messages start with a client id such as pick:reqmenu, which means nothing to the user.

  • escape="false" and itemLabelEscaped="false" with user text. Cross-site scripting. Escape, or sanitise on the way in and render with the default.

  • Select lists that change between request and postback. Fails with "Value is not valid". Build the list from state that does not change within the view, and make equals() of object values consistent with the converter.

  • Objects as item values without a converter. "Conversion Error setting value …​ for 'null Converter'". Register a converter for the class, or submit the id.

  • Relying on generated ids (j_idt21). Give an explicit id to anything you refer to.

  • Business logic in getters bound to value. A getter runs several times per request for each component, in both rendering and decoding; the lifecycle page explains why. Compute in an action or @PostConstruct and return a field.

  • Hidden fields as a security measure. The browser can send anything; recheck server-side.

  • A state-changing h:link or h:button. GET must not change state.

  • h:commandLink for plain navigation. It requires JavaScript, is not crawlable and cannot open in a new tab. Use h:link.

  • binding on a bean that outlives the request. Components are not thread-safe or serialisable in the way scoped beans expect (architecture page).

  • Too much in one h:panelGrid. A table per field row is hard to style; consider CSS grid on a div with h:panelGroup layout="block" children.

What Changed Between Versions

Version Change for the standard HTML tags

1.x

The tags were used from JSP pages through tag handlers, with the namespaces http://java.sun.com/jsf/html and http://java.sun.com/jsf/core.

2.0

Facelets becomes the standard view technology. The VDL documentation marks h:button, h:link, h:outputScript, h:outputStylesheet and the name and library attributes of h:graphicImage as added in 2.0.

2.1

The VDL documentation marks h:doctype as added in 2.1.

2.2

h:inputFile; the role attribute; HTML5 pass-through attributes and elements (the jakarta.faces.passthrough namespace).

2.3

h:commandScript; the group attribute of h:selectOneRadio (standalone radio buttons); styleClass on h:column; disabled on h:button.

3.0

The namespaces become jakarta.faces.*; the tag behaviour is unchanged.

4.0

Namespace URNs jakarta.faces.html, jakarta.faces.core, jakarta.faces.facelets replace the http://xmlns.jcp.org/jsf/* and http://java.sun.com/jsf/* URIs (old ones are planned for removal in 5.0). New: type on h:inputText, multiple and accept on h:inputFile, f:selectItemGroup and f:selectItemGroups, layout="list" for h:selectOneRadio and h:selectManyCheckbox.

4.1

rowStatePreserved and the id attribute of h:head and h:body are added to the VDL documentation (the 4.1 VDL says the id of those two tags "is only rendered when the current doctype is a HTML5 doctype"). No new h tag.

5.0 (milestones)

Not released. Planned work is listed on the versions page; none of it is assumed here. The specification page lists a placeholder attribute for h:inputText and h:inputTextarea (issue 2025) and oninput-style HTML5 events for UIInput (issue 1507) as planned; neither exists in 4.1, so use a pass-through attribute for them today (HTML5 page).

Where the Books Are Outdated

The standard component library is the part of the books that has aged best, because the tags are stable. Still:

  • JavaServer Faces 2.0: The Complete Reference (Burns and Schalk), which has a full reference of the standard components and chapters on selection and output components, was written for Faces 2.0. It does not have the type attribute of h:inputText, layout="list", f:selectItemGroups, the group attribute of h:selectOneRadio, multiple and accept on h:inputFile, h:commandScript, the jakarta.faces.* namespace URNs, or the jakarta. package names (its Java uses javax.faces). Its tag reference is still a useful guide to the older attributes.

  • The books that predate Faces 2.0 (the Mann and the Dudney books) show JSP pages with <%@ taglib uri="http://java.sun.com/jsf/html" %>, the f:view wrapper and h:outputText for every piece of text. JSP views were removed in 4.0, and on Facelets expressions work in template text. The component model behind the tags is the same, which is the reason to keep those books for background.

  • Recipes in the JSF 2.0 Cookbook and similar books that add file upload, rich tables or selection widgets with Tomahawk (t:), Trinidad or RichFaces components target libraries that no longer exist; with the standard tags, use h:inputFile, layout="list" and CSS, and for richer widgets the PrimeFaces components (PrimeFaces pages, other libraries).

  • The PrimeFaces Cookbook shows p: components that stand in for the standard ones (p:inputText, p:selectOneMenu, p:commandButton); the standard tags remain the base, and PrimeFaces 16 builds on them.

Use the books for the explanation of the component model and the way the tags are meant to be combined, and the VDL documentation and this page for the attribute lists and rendered HTML of Faces 4.1.

Source: JavaServer Faces 2.0: The Complete Reference, McGraw-Hill, publisher page