HTML5-Friendly Markup
|
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), Eclipse Mojarra
4.1.17 and Apache MyFaces Core 4.1.4. Faces 4.0 behaves the same except where the text says otherwise. The behavior is
taken from the specification, the Jakarta EE Tutorial and the VDL, and every example was run on 2026-10-08 in a scratch
Bookshelf WAR on Tomcat 11.0.26 with Weld 6.0.4.Final, once with Mojarra 4.1.17 and once with MyFaces 4.1.4, and requested
with curl. The rendered output shown is captured from those runs; the order of the attributes differs between the two
implementations, and the jsessionid and view state values are omitted.
The component model of Faces hides HTML behind components: h:inputText decides which <input> element and which
attributes reach the browser. HTML5 added input types (email, date, number), attributes (placeholder,
autofocus, pattern) and whole elements (<meter>, <details>) that the standard components did not know. Faces
2.2 answered with the HTML5-friendly markup feature: instead of new components that imitate HTML5, the view author
may write HTML5 directly and still get the server-side component behind it. It has two halves, which are mirror images
of each other.
| Half | You write | You get |
|---|---|---|
Pass-through attributes |
A Faces component tag with extra attributes the component does not know. |
The component and renderer you chose, with the extra attributes copied to the browser untouched. |
Pass-through elements |
A plain HTML element with at least one attribute from the Faces namespace. |
The HTML you wrote, backed by a real server-side component that takes part in the lifecycle. |
Namespaces
Both halves are switched on by XML namespaces on the root element. Jakarta Faces 4.0 renamed them from the
http://xmlns.jcp.org/... URIs to short jakarta.faces... URNs.
| Purpose | Faces 4.0 and later | Faces 2.2 and 3.x | Usual prefix |
|---|---|---|---|
Pass-through attributes |
|
|
|
Pass-through elements (attributes on plain HTML) |
|
|
|
Standard components |
|
|
|
The old URIs still work in 4.x but are planned for removal in Faces 5.0, so new pages should use the URNs. A page
that uses both halves starts like this (the HTML5 <!DOCTYPE html> is plain and Facelets accepts it):
<!DOCTYPE html>
<html lang="en"
xmlns:faces="jakarta.faces"
xmlns:f="jakarta.faces.core"
xmlns:h="jakarta.faces.html"
xmlns:p="jakarta.faces.passthrough">
A page that declared the old jsf, core, html and passthrough URIs of Faces 2.2 and 3.x still produced a pass-through
element (jsf:id, jsf:value) and a pass-through attribute (p:type) on Mojarra 4.1.17 and MyFaces 4.1.4. The other namespaces
are explained on the Facelets page.
Pass-through Attributes
For a component tag, the attributes it supports are fixed by its component and renderer. Some are interpreted (the
columns attribute of h:panelGrid), some are copied to the output (the lang attribute of h:inputText). Anything
else used to be dropped or ignored. A pass-through attribute is a name and value that the renderer copies to the
browser without interpreting it. The specification offers three ways to declare one.
| Syntax | Use it when |
|---|---|
|
The attribute is fixed in the page; this is the common form. |
|
The attribute name or value is computed, or you want to add a single attribute conditionally with |
|
A whole group of attributes comes from a bean. The EL expression must evaluate to a |
The next input for the Bookshelf review form uses all three styles. The first two are equivalent; the third supplies the attributes from a map.
<!-- 1. namespace prefix -->
<h:inputText id="rating" value="#{reviewForm.rating}"
p:type="number" p:min="1" p:max="5"
p:title="Enter a rating from 1 to 5."/>
<!-- 2. nested single attributes -->
<h:inputText id="rating2" value="#{reviewForm.rating}">
<f:passThroughAttribute name="type" value="number"/>
<f:passThroughAttribute name="min" value="1"/>
<f:passThroughAttribute name="max" value="5"/>
</h:inputText>
<!-- 3. a Map<String, Object> from a bean -->
<h:inputText id="rating3" value="#{reviewForm.rating}">
<f:passThroughAttributes value="#{reviewForm.ratingAttributes}"/>
</h:inputText>
Each renders, on Mojarra, <input id="reviewForm:rating" name="reviewForm:rating" value="3" type="number" min="1" max="5" title="Enter a rating from 1 to 5." />
(the form had the id reviewForm; the second and third inputs lack the title); MyFaces wrote the same attributes in another
order. The type="number" replaces the default type="text" that h:inputText would otherwise write, which is the usual
reason to use a pass-through attribute: pick a better HTML5 input type while keeping the server-side conversion,
validation and value binding of h:inputText. Pass-through attribute values may use EL, as the specification’s
pt:autofocus="#{config.autofocus}" example shows: with p:autofocus="#{reviewForm.autofocus}" and a boolean
true, both implementations wrote autofocus="true". The name, however, is always a literal.
Because the browser, not Faces, interprets these attributes, the Faces behavior does not change. An HTML5 min and max
are checks the browser makes before it submits; they are not validators. The server still needs f:validateLongRange
or Bean Validation, as a request can be forged, as the lifecycle page
explains. The validators and converters are covered on the validators and
converters pages.
Pass-through Elements
The converse: you write an HTML element and mark it as a Faces element by giving it at least one attribute from the
jakarta.faces namespace. Facelets then replaces it, during view build, with the matching standard component, but the
output is the markup you wrote. The most useful marker is faces:id, as it gives the component the identifier that
postback processing, validation messages and Ajax need.
<input type="email" faces:id="email" faces:value="#{reviewForm.email}"
required="required" placeholder="you@example.org"/>
Here the <input> is an h:inputText in the component tree (the type="email" selects it) with the HTML you typed as
its output. The value binding is written faces:value, not value: the attributes in the faces namespace become the
attributes of the component, and every other attribute is a pass-through attribute. A plain value="#{reviewForm.email}"
next to faces:id rendered the element but never bound it: in the run, a submitted address did not reach the bean on either
implementation, while with faces:value the bean property was set (email=x@y.org) and the value was written back to the
field. Other faces: attributes also trigger the transformation and pick the component; faces:action on an <a>
makes an h:commandLink and faces:outcome on an <a> makes an h:link. The element name plus the identifying
attribute selects the component:
| HTML element | Identifying attribute | Component |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
none |
|
|
|
|
|
none |
|
|
none |
|
|
none |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
none, or |
|
|
none |
|
Source: the table "How Facelets Renders HTML5 Elements" in the Jakarta EE Tutorial section named above. The rows for a with faces:action, button with faces:outcome and input of type email were run on both implementations and produced a command link, a button that navigates to the outcome and an input component; a meter with faces:id rendered as a meter.
An element that is not in the table, such as <meter>, still works: with a faces: attribute it is turned into a
generic element component (faces:element in the tag library, rendered by the jakarta.faces.passthrough.Element
renderer), so the specification’s example <meter faces:id="meter" min="#{bean.min}" max="#{bean.max}" value="350">
is a valid view. In every case you keep the lifecycle: the component is in the tree, receives its submitted value, is
converted and validated, and can carry f:ajax, f:validateLength or f:convertNumber children.
Without any faces: attribute an <input> is ordinary template text, not a component. It is written to the output as
is and never takes part in the lifecycle, so a typed value is not bound to the bean. In the run, <input type="text" id="plain" name="plain" value="stays template text"/>
came out unchanged on both implementations, next to the converted elements. This is the cause of the common
question "my HTML form does not reach the backing bean". Add faces:id and faces:value="#{...}" to bind it.
The name you give an element is not safe to rely on. The element <input type="email" faces:id="email" name="email" .../>
inside <form faces:id="reviewForm"> was rendered on Mojarra as name="reviewForm:email", the client identifier that decoding
looks for, so the hand-written name was replaced. MyFaces kept the hand-written name="email", and a postback with the field
email did not set the bean property, as did one with reviewForm:email. So leave out name on an element that carries a
faces: attribute, and let the renderer write it.
Choose between the two halves by who owns the markup. With pass-through attributes the component and renderer own it
and you decorate. With pass-through elements you own it, including the closing of the form and the HTML name
attribute; the renderer fills in the component identifier. A designer who hands you finished HTML5 will prefer elements;
a team that already uses h: tags everywhere will add p: attributes.
The type Attribute of h:inputText
Before Faces 4.0 the only way to get type="email" or type="date" out of h:inputText was p:type or a
pass-through element. The VDL of Faces 4.0 and 4.1 lists a type attribute on h:inputText itself (the 3.0 VDL does not):
"Controls the data type and associated control of the element", with the default text. The VDL adds that when the value
names a type for which a more specific component exists (hidden, password, checkbox, radio, file, submit,
image, reset and button) and the project stage is Development, the renderer must add a warning that the more specific
component should be preferred. So the common case is plain.
<h:inputText id="published" type="date" value="#{reviewForm.published}">
<f:convertDateTime type="localDate" pattern="yyyy-MM-dd"/>
</h:inputText>
<h:inputText id="email" type="email" value="#{reviewForm.email}"/>
<h:inputText id="pages" type="number" value="#{reviewForm.pages}"/>
<h:inputText id="nodefault" value="#{reviewForm.email}"/>
Both implementations rendered, with the bean’s date 1965-08-01 and 412 pages:
<input id="reviewForm:published" type="date" name="reviewForm:published" value="1965-08-01" />
<input id="reviewForm:email" type="email" name="reviewForm:email" value="" />
<input id="reviewForm:pages" type="number" name="reviewForm:pages" value="412" />
<input id="reviewForm:nodefault" type="text" name="reviewForm:nodefault" value="" />
(The attribute order and the placement of name differ slightly on MyFaces.) This is only the rendered HTML type; the
conversion still comes from the bound property or from f:convertDateTime / f:convertNumber. The VDL of
f:convertDateTime accepts type="localDate" among the java.time values, and the run confirms the advice for dates: a
type="date" browser control submits yyyy-MM-dd, and the converter above parsed 1966-01-02 into the LocalDate
property on both implementations. Without the converter the same submission failed, in different ways: Mojarra reported a
conversion error with null Converter in its message, and MyFaces answered with a 500 whose cause was
Cannot convert [1966-01-02] of type [class java.lang.String] to [class java.time.LocalDate]. A text that does not match the
pattern (01/02/1966) was reported on both as could not be understood as a date. On a Faces 2.2 or 3.x server use
p:type="date".
placeholder, data-* and Other HTML5 Attributes
Every HTML5 attribute that a Faces tag does not support is a pass-through attribute. The ones that come up most often:
<h:inputText id="isbn" value="#{reviewForm.isbn}"
p:placeholder="978-..."
p:autofocus="autofocus"
p:autocomplete="off"
p:pattern="[0-9\-]{10,17}"
p:inputmode="numeric"
p:data-book-id="#{reviewForm.bookId}"/>
Both implementations wrote every attribute, with the EL value resolved:
<input id="reviewForm:isbn" type="text" name="reviewForm:isbn" value="" placeholder="978-..." autofocus="autofocus" autocomplete="off" pattern="[0-9\-]{10,17}" inputmode="numeric" data-book-id="42" />
-
p:placeholdergives a hint inside the empty field. Do not use it as the only label: it disappears while the user types. -
p:autofocusandp:autocompleteare the HTML5 attributes of the same name, passed to the browser as written. -
p:pattern,p:min,p:max,p:step,p:maxlengthandp:requiredare browser-side validation. The Facesrequired="true"is the server-side one; set both to get fast feedback and safe validation. -
p:data-*attributes carry data to JavaScript, for instancep:data-book-id. The name is a literal that may contain hyphens; the value is an EL expression evaluated when the view renders. -
A form with
p:novalidate="novalidate"switches the browser’s built-in validation off, which is useful when you want Faces messages to be the only ones shown. Onh:formboth implementations renderednovalidate="novalidate"on theformelement.
Accessibility Attributes
Accessibility attributes are HTML5 attributes, so they are pass-through attributes. The standard components already
carry some of them and the rest come through p:.
| Need | Markup |
|---|---|
A visible label tied to the input |
|
An accessible name when there is no visible label |
|
The relation to a message or help text |
|
Required, invalid, live regions |
|
Meaningful images |
|
Page language |
|
A form that used them, run on both implementations:
<h:form id="reviewForm" p:novalidate="novalidate">
<h:outputLabel for="email" value="Email"/>
<h:inputText id="email" value="#{reviewForm.email}" required="true" p:type="email"
p:aria-required="true" p:aria-describedby="reviewForm:emailMsg"/>
<h:message id="emailMsg" for="email" p:role="alert" p:aria-live="polite"/>
<h:inputText id="search" value="#{reviewForm.isbn}" p:aria-label="Search the catalogue"/>
<h:graphicImage name="cover.png" alt="Cover of Dune"/>
</h:form>
rendered (Mojarra, the attribute order differing on MyFaces):
<label for="reviewForm:email">Email</label>
<input id="reviewForm:email" name="reviewForm:email" value="" type="email" aria-required="true" aria-describedby="reviewForm:emailMsg" />
<span id="reviewForm:emailMsg" role="alert" aria-live="polite"></span>
<input id="reviewForm:search" type="text" name="reviewForm:search" value="" aria-label="Search the catalogue" />
<img src="RES_NOT_FOUND" alt="Cover of Dune" />
The for was expanded to the client identifier, the ARIA attributes arrived as written, and the span of the empty
h:message was rendered with its role because it carries attributes. (The RES_NOT_FOUND src is only because the scratch
application has no cover.png resource; Mojarra added a warning about it in the Development stage.) The
Accessibility page covers the wider topic. This page only shows that Faces does not stand in the
way: every ARIA attribute is one p: prefix away. Whether the resulting page is accessible still has to be checked in the
browser with a screen reader or an audit tool.
The jsfc Attribute: the Historical Precursor
Pass-through elements were not the first attempt at letting designers keep their HTML. Facelets 1.x, the standalone
view framework that predates JSF 2.0 and is described by Aranda and Wadia in Facelets Essentials, had the jsfc
attribute. Its value names the component tag that should replace the element:
<input type="text" jsfc="h:inputText" value="#{user.name}"/>
<a href="#" jsfc="h:commandLink" action="#{user.save}">Save</a>
The page remains valid, renderable HTML in a designer’s tool, while Facelets compiles it into the component. That
advantage, mock-up friendliness, is the same one pass-through elements give. The differences explain why jsfc was
superseded:
| Aspect | jsfc (Facelets 1.x) |
Pass-through elements (Faces 2.2 and later) |
|---|---|---|
Which component |
Named by the author: |
Inferred from the element and an identifying attribute, according to the table above. |
Specified by |
A Facelets library feature, not part of the Faces specification. |
The Faces specification, in the Facelets TagDecorator API. |
Output |
The component’s renderer decides. In the run below, |
The author’s markup is the output; the component is there for the lifecycle. |
Status |
Legacy. |
Standard. |
It is still accepted: this page
<form jsfc="h:form" id="reviewForm">
<input type="text" jsfc="h:inputText" id="isbn" value="#{reviewForm.isbn}" class="x"/>
<a href="#" jsfc="h:commandLink" action="#{reviewForm.save}">Save</a>
</form>
rendered on Mojarra 4.1.17 and MyFaces 4.1.4 as a form with an h:inputText (id="reviewForm:isbn", class="x") and a command
link, and neither response contained the jsfc attribute. That is a legacy compatibility path and is not mentioned in the
specification’s view-authoring chapter, so do not use jsfc in new code. When migrating a Facelets 1.x page, replace
each jsfc="h:inputText" element by a faces:id element or by the h: tag with p: attributes. Most of the book is
otherwise out of date for JSF 2.2 and later, as it describes standalone Facelets with its own ViewHandler. The
templating page lists the rest of what changed since the book.
Source: Bruno Aranda and Zubin Wadia, Facelets Essentials (Apress, 2008, ISBN 978-1-4302-1049-8), and a scratch run of the page above on both implementations.
What Changed Between Versions
| Version | Change relevant to HTML5-friendly markup |
|---|---|
JSF 2.2 |
HTML5-friendly markup is introduced: pass-through attributes ( |
Faces 3.0 |
The |
Faces 4.0 |
The namespaces become |
Faces 4.1 |
No change to this feature. |
Faces 5.0 (upcoming, milestone |
Planned: removal of the old XML namespaces (issue 2105 on the milestone page). Not current behaviour. |
Where the Books Are Outdated
The books consulted for this section predate HTML5-friendly markup or predate its current form.
-
Aranda and Wadia, Facelets Essentials (2008). Describes the
jsfcattribute as the way to keep HTML in a designer’s tool (see The jsfc Attribute: the Historical Precursor). Pass-through elements are the standard replacement. -
Burns and Schalk, JavaServer Faces 2.0: The Complete Reference (2010) and Leonard, JSF 2.0 Cookbook (2010). Both describe JSF 2.0, which predates both pass-through attributes and pass-through elements (JSF 2.2). The
h:inputTextattributetypeis newer still (Faces 4.0). -
The PrimeFaces books. Use the
p:prefix for the PrimeFaces tag library (http://primefaces.org/ui). Give the pass-through namespace another prefix, as the namespaces section of the Facelets page explains.
Pitfalls
-
Namespaces mixed up.
p:must be bound tojakarta.faces.passthroughandfaces:tojakarta.faces. A prefix bound to the wrong URI is not a pass-through namespace, and the attribute is not treated as one; the oldjcpURIs are planned for removal in Faces 5.0. -
Ids. On a pass-through element the HTML
idattribute andfaces:idare different. Usefaces:idand let Faces produce theidandname; on MyFaces a hand-writtennameis kept and prevents postback binding. -
valuewithout the prefix. On a pass-through element a barevalue="#{bean.x}"is a pass-through attribute, not the value of the component. Writefaces:value. -
A required HTML attribute and an empty submission.
required="required"in the browser stops an empty submit before Faces sees it; if you want the Faces message, addnovalidateor use only the Facesrequired. -
Expression of the attribute name. Names cannot be computed on a
p:attribute. Usef:passThroughAttributeorf:passThroughAttributeswhen the name varies. -
Server validation. A pass-through
min,max,patternormaxlengthis advice to the browser. Validate on the server too. -
XML well-formedness. The page is XML. Attributes with no value (
required,autofocus) must be writtenrequired="required"; a bare attribute is a parse error.
References
-
Jakarta Faces 4.1 specification, section 10.1.3, HTML5 Friendly Markup
-
Jakarta Faces 4.1 tag library documentation (VDL),
f:passThroughAttribute,f:passThroughAttributes,faces:element -
Jakarta Faces 4.1 VDL: h:inputText and Jakarta Faces 4.0 VDL: h:inputText (the
typeattribute) -
Jakarta Faces 4.1 VDL: f:convertDateTime, f:passThroughAttribute and f:passThroughAttributes
-
Jakarta Faces 4.1 specification, 10.1.3 HTML5 Friendly Markup
-
Facelets Essentials (Aranda and Wadia, Apress, 2008), the historical
jsfcattribute