Facelets & XHTML Views
|
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. |
Every page of a Jakarta Faces application that is not written in Java is a Facelets page: an XHTML file in which ordinary HTML elements are mixed with tags that stand for server-side components, and in which text and attribute values may contain Expression Language. This page is about the file itself — what Facelets is, what it turns the file into, what the file must look like to be accepted, which XML namespaces make a tag mean something, and how comments and special characters are handled. The tags that build layouts (Facelets Templating), the expression syntax (Expression Language), the HTML5 pass-through markup (HTML5-Friendly Markup), the JSTL tags (JSTL and Build Time vs Render Time) and the Java-based alternative to XHTML (Programmatic Facelets) have their own pages and are only named here.
The page is written against Jakarta Faces 4.1 (API jakarta.faces-api 4.1.2, Jakarta EE 11, Java 17 or later), Eclipse
Mojarra 4.1.17 and Apache MyFaces Core 4.1.4. Faces 4.0 (Jakarta EE 10, Java 11 or later) behaves the same for
everything here except where What Changed Between Versions says otherwise. Faces 5.0 (Jakarta EE 12) is still a milestone
(5.0.0-M9, the latest on 2026-10-08), so what is said about it is "upcoming", never current behaviour. The examples
were run in a scratch Maven WAR on Mojarra 4.1.17, Weld 6.0.4.Final and Apache Tomcat 11.0.26 (embedded), and every
piece of generated HTML shown below was captured from that run with curl. The doctype, XHTML-rule, namespace, comment,
escaping and f:view examples, and the new measurement in Components Are Not Re-Created on Redisplay, were repeated on MyFaces 4.1.4 on 2026-10-08, and
where the two answered differently the text says so (collected in Mojarra and MyFaces). A statement tagged "Mojarra"
or "observed on Mojarra" that is not repeated for MyFaces was not run there, and the specification text is quoted for what
both implementations must do. The running scenario is the original Bookshelf, a catalogue of books, authors and
reviews.
What Facelets Is
Facelets is the view declaration language (VDL) of Jakarta Faces: the language in which a view is written. The
specification defines a VDL as the thing that knows how to create a view from a view identifier, how to build the
component tree of that view and how to render it, and it defines Facelets as the VDL that must be present in every
implementation. Pages are authored in XHTML; a Facelets page is not a program, and it is not sent to the browser as
it is written. It is compiled into a tree of objects, and that tree, when it is run, creates the UIComponent tree of
the view and so describes it.
XHTML with tags and EL"] compile["Compile once
SAX parse into a
tree of tag handlers"] build["Build view
run the handlers,
create UIComponents"] render["Render response
each component
writes its markup"] html["HTML to the browser"] file --> compile --> build --> render --> html compile -.->|"recompiled when the file changes
after the refresh period"| file
Three properties of that pipeline explain most of what follows.
-
The file is parsed as XML. The compiler uses an XML parser. Malformed XML is not tolerated the way a browser tolerates malformed HTML; it is a compile error with a line number (see The page must be well-formed XML).
-
A page is compiled once and built many times. The compiled form is cached. On every request that needs the view built, the tag handlers are run again against the current state, which is why a tag such as
c:ifthat is decided at build time behaves differently from an attribute such asrenderedthat is decided at render time. The JSTL page covers that difference in full, and Components Are Not Re-Created on Redisplay shows the specification rule behind it. -
Tag attributes are not declared. In Facelets, tag attributes "automatically map to properties, attributes and ValueExpressions on UIComponent instances", according to the specification, so a new attribute on a component needs no tag library descriptor.
The Jakarta EE Tutorial lists what Facelets offers on top of that: XHTML pages, Facelets tag libraries in addition to the Faces and JSTL ones, Expression Language, templating for components and pages, code reuse through templates and composite components, compile-time validation of EL and a faster compilation than Jakarta Server Pages.
Facelets Compared with Jakarta Server Pages
Jakarta Faces 1.x used Jakarta Server Pages (JSP) as its view technology, and the two did not fit together well. A JSP page is compiled to a servlet that runs once per render, and the component tree is built by the order in which the tag handlers of the servlet execute, interleaved with the template text; Facelets compiles to a tree that builds the component tree and so has the whole tree in hand. The specification lists the differences itself:
| Feature | Jakarta Server Pages | Facelets |
|---|---|---|
Page is compiled to |
A servlet, executed each time the page renders; the component tree is built by the custom tags in the page |
An abstract syntax tree that, when executed, builds the component tree |
Tag attributes |
Every attribute must be declared in a TLD |
Dynamic: attributes map to properties, attributes and value expressions of the component |
Page templating |
Not supported, must go outside core JSP |
A core feature |
Performance |
Slower, because of the servlet-per-page technique |
Simpler and faster |
Expressions in template text |
Cause unexpected behaviour |
Work as expected |
A Jakarta standard |
Yes, the specification is separate from the implementation |
Part of the Faces specification itself |
The consequence that matters most when you read old code: in JSP, an EL expression in template text was evaluated by
the JSP engine, not by Faces, and was evaluated before the components it referred to existed. In Facelets, template
text containing an expression becomes an h:outputText component, as The XHTML Rules shows.
Jakarta Server Pages views were deprecated in JSF 2.0 and are removed in Faces 4.0. A migration therefore turns every
.jsp view into a Facelets view; there is no option to keep both in a 4.x application.
The Anatomy of a Facelets Page
The smallest useful page of the Bookshelf shows a book title that comes from a CDI bean. The bean is the one used for all the examples on this page.
package org.example.bookshelf.web;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Named;
@Named
@RequestScoped
public class TitleBean {
private String q;
public String getTitle() {
return "Dune & <Messiah>";
}
public String getQ() {
return q;
}
public void setQ(String q) {
this.q = q;
}
}
The title deliberately contains & and angle brackets, because the interesting question about any output is what
happens to them (Escaping). Here is the page.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html"
xmlns:f="jakarta.faces.core">
<h:head>
<title>Bookshelf</title>
</h:head>
<h:body>
<p>Title: #{titleBean.title}</p>
<h:outputText value="#{titleBean.title}"/>
</h:body>
</html>
Reading it from the top:
-
The XML declaration is optional but, when present, must be the very first thing in the file. Without it the parser assumes UTF-8, which is also what you want.
-
The
DOCTYPEis covered in The XML declaration and the doctype.<!DOCTYPE html>is the HTML5 form and the one to use. -
The root element is the ordinary
htmlelement in the XHTML namespace. Everything that is not in a Faces namespace is copied through to the output. -
Each
xmlns:attribute binds a prefix to a tag library (Namespaces). A prefix that is used but not bound is a parse error. -
h:headandh:bodyreplaceheadandbodyand are what makes Faces able to place scripts and style sheets (h:head and h:body). -
#{titleBean.title}in the middle of the paragraph is template text containing an expression, and theh:outputTextbelow it prints the same value.
Running the page on Mojarra 4.1.17 produces this response:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<title>Bookshelf</title></head><body>
<p>Title: Dune & <Messiah></p>Dune & <Messiah></body>
</html>
The h: and f: prefixes are gone, h:head and h:body became head and body, the xmlns:h and xmlns:f
declarations were removed, and the XML declaration was passed through. Because the bean text was Dune & <Messiah>,
both the template text and the component print Dune & <Messiah>: the output is escaped by default, in template text as well as in h:outputText. The two
forms differ only in that the second is a component in the tree and so can carry an id, a rendered condition, a
converter and a style.
File extension and view identifier
A Facelets page is selected by its file name. The default suffix is .xhtml, so a request for /book.xhtml (or, with a
prefix mapping, /faces/book.xhtml) renders the file /book.xhtml of the web application. Two context parameters
change that:
-
jakarta.faces.FACELETS_SUFFIXsets the suffix or the list of suffixes of files that contain Facelets markup. -
jakarta.faces.FACELETS_VIEW_MAPPINGSis a semicolon separated list of patterns that are forced to be Facelets, whatever their extension.
The same file extension can be given its own processing mode in faces-config.xml, which is how the doctype rules of
the next section are changed.
The XHTML Rules
The specification requires the runtime to accept every page that conforms to the XHTML 1.0 Transitional DTD, and the Jakarta EE Tutorial says the same. In practice that means these rules, each checked on Mojarra 4.1.17:
The page must be well-formed XML
Every element is closed, attribute values are quoted, element names are lower case, and & and < in text and in
attribute values are written as & and <. The classic casualty is the HTML void element. This page is
valid HTML5 and is rejected by Facelets:
<!DOCTYPE html>
<html lang="en" xmlns:h="jakarta.faces.html" xmlns:f="jakarta.faces.core">
<h:head><title>Bookshelf</title></h:head>
<h:body>
<p>x<br>y</p>
</h:body>
</html>
The response has HTTP status 500 and the title of the error page names the cause and the line:
Error Parsing /b.xhtml: Error Traced[line: 7] The element type "br" must be terminated by the matching end-tag "</br>".
The fix is <br/>. The same applies to img, input, hr, meta and link. A second parse error that is common when
a page is edited by hand is a prefix that is used and not declared:
Error Parsing /f.xhtml: Error Traced[line: 6] The prefix "h" for element "h:outputText" is not bound.
Because the failure happens when the file is compiled, it appears on the first request for the page, not when the
application starts. In the Development project stage the error page also carries the stack trace and the scoped variables.
MyFaces 4.1.4 reported the same messages, with the view id of the page prefixed by /viewMetadata.
Expressions in text become components
An expression in template text is not left to the browser. The specification says the runtime must treat an EL expression
that appears in the page without being the right-hand side of a tag attribute "as if" it appeared in the value
attribute of an h:outputText in the jakarta.faces.html namespace, whether or not that namespace was declared.
That is why the p element above could print the title without an h: tag around it.
The implementations are free to compile this more cheaply than a real h:outputText. A page with <p>Title: #{titleBean.title}</p>
and an explicit h:outputText printed the component tree of the view (a bean method walked UIViewRoot and printed
the class of each component). On Mojarra 4.1.17 and on MyFaces 4.1.4 the paragraph and its expression were part of a
UIInstructions component, which holds the literal text and the expressions of one stretch of markup, and only the
explicit tag produced an HtmlOutputText:
UIViewRoot
UIInstructions
HtmlBody
UIInstructions <- the <p> with its expression
HtmlOutputText <- the explicit h:outputText
UIInstructions
So an expression in text behaves like an output without an id, a rendered condition or a converter. When any of those is
needed, write the h:outputText. (The UIOutput and HtmlHead entries of the head differ between the two implementations
and are omitted.)
Source: Jakarta Faces 4.1 specification, 10.3 XHTML Specification for Facelets and a scratch run on both implementations
The XML declaration and the doctype
A Facelets file may start with an XML declaration and a doctype. What the page sends to the browser is a separate
question and is governed by the processing mode of the file extension. The specification defines four, selected with
facelets-processing in faces-config.xml, and html5 is the default:
process-as value |
html5 (default) |
xhtml |
xml |
jspx |
|---|---|---|---|---|
XML doctype |
Simplified to |
Passed through |
Consumed |
Consumed |
XML declaration |
Passed through |
Passed through |
Consumed |
Consumed |
Processing instructions |
Passed through |
Passed through |
Consumed |
Consumed |
CDATA section start and end tags |
Passed through |
Passed through |
Consumed |
Consumed |
Escaping of inline text |
Escaped |
Escaped |
Escaped |
Not escaped |
XML comments |
Passed through |
Passed through |
Consumed |
Consumed |
"Passed through" means the markup reaches the browser unmodified and "consumed" means it is read and dropped on the
server. The jspx mode exists to ease the migration of applications that used the XML syntax of JSP.
The effect is easy to see. This page declares the long XHTML 1.0 Transitional doctype:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html">
<h:head><title>Bookshelf</title></h:head>
<h:body>Hello</h:body>
</html>
With the default html5 mode the first lines of the response are
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Bookshelf</title></head><body>Hello</body>
</html>
Mapping .xhtml to the xhtml mode brings the long doctype back unchanged. Nothing but this is needed in
faces-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_1.xsd"
version="4.1">
<faces-config-extension>
<facelets-processing>
<file-extension>.xhtml</file-extension>
<process-as>xhtml</process-as>
</facelets-processing>
</faces-config-extension>
</faces-config>
There is rarely a reason to do it. Write <!DOCTYPE html> in the source: it keeps browsers out of quirks mode, it
needs no mode switch and it is what the html5 mode sends anyway. The first lines of the response were identical on MyFaces
4.1.4, with the same simplified doctype in the default mode and the long one after the mapping to xhtml.
Character entities
An XML parser only knows five named entities: &, <, >, " and '. Every other
named HTML entity, © or for example, is known only if the document type declares it, and the
<!DOCTYPE html> of HTML5 does not. On Mojarra 4.1.17 a page with the HTML5 doctype and a © in the body was
rejected with the error
Error Parsing /cp.xhtml: Error Traced[line: 4] The entity "copy" was referenced, but not declared.
while the same text with the long XHTML 1.0 Transitional doctype (which declares the HTML entities) was accepted and
sent as the character itself. Numeric character references need no declaration: ©, © and  
were all accepted with the HTML5 doctype and sent as the characters. So, with <!DOCTYPE html>, write numeric
references, or type the character directly in a UTF-8 file. MyFaces 4.1.4 gave the same error for ©, accepted the
character with the long doctype, and accepted the numeric references.
Namespaces
A tag means something only through its namespace. The prefix is a local alias and is conventional, not mandatory:
xmlns:composite="jakarta.faces.composite" is as good as xmlns:cc=.... The current namespaces, which are plain
URNs without a scheme and are the ones to use since Faces 4.0, are:
| Namespace (Faces 4.0 and later) | Prefix | Contents | Replaces (Faces 2.x and 3.x) |
|---|---|---|---|
|
|
Component tags for the standard HTML render kit: |
|
|
|
Render kit independent tags: |
|
|
|
Templating: |
|
|
|
Composite component declaration: |
|
|
|
Pass-through attributes for HTML5 markup ( |
|
|
|
Pass-through elements: turn a plain HTML element into a component ( |
|
|
|
The subset of the Jakarta Tags (JSTL) core library that Facelets exposes: |
|
|
|
The whole Jakarta Tags function library: |
|
The conventional p: prefix of jakarta.faces.passthrough is also the conventional prefix of PrimeFaces
(xmlns:p="http://primefaces.org/ui", see the PrimeFaces setup page). A prefix is only an alias, so when both appear in one page give one of them
another prefix, pt: for example. The HTML5-friendly markup page
covers pass-through markup.
The old URIs still work in 4.1
Pages written for Faces 2.x and 3.x declare xmlns:h="http://xmlns.jcp.org/jsf/html", and the Faces 2.0 pages of the older
books declare http://java.sun.com/jsf/html. Both are still accepted by Faces 4.1: a page that declares the
xmlns.jcp.org URI for h: and the java.sun.com URI for f: rendered correctly on Mojarra 4.1.17, with the locale="de" of f:view
taking effect and f:metadata accepted. Nothing needs to be changed to run an old page on Faces 4.0
or 4.1 apart from the javax.faces to jakarta.faces package rename in Java code.
Do not rely on that for new code. Support for the old namespaces is planned to be removed in Faces 5.0, which is an upcoming release; switching now is a mechanical change of the attribute values, for example
# Run from the project root. -i '' is the BSD/macOS form; on GNU sed use -i.
find src -name '*.xhtml' -exec sed -i '' \
-e 's#http://xmlns.jcp.org/jsf/html#jakarta.faces.html#g' \
-e 's#http://xmlns.jcp.org/jsf/core#jakarta.faces.core#g' \
-e 's#http://xmlns.jcp.org/jsf/facelets#jakarta.faces.facelets#g' \
-e 's#http://xmlns.jcp.org/jsf/composite#jakarta.faces.composite#g' \
-e 's#http://xmlns.jcp.org/jsf/passthrough#jakarta.faces.passthrough#g' \
-e 's#http://xmlns.jcp.org/jsp/jstl/core#jakarta.tags.core#g' \
-e 's#http://xmlns.jcp.org/jsp/jstl/functions#jakarta.tags.functions#g' {} +
Run it on a clean working tree and read the diff. The java.sun.com forms need the same treatment. A composite
component library is declared by appending the library name to the namespace, and the specification gives the form
xmlns:ez="jakarta.faces.composite/ezcomp"; the script above also rewrites the prefix of a declaration such as
http://xmlns.jcp.org/jsf/composite/ezcomp, but search for composite/ afterwards and check each result by hand.
What a wrong namespace does
An undeclared prefix is an error at compile time (see The page must be well-formed XML), but a prefix bound to a namespace that no tag
library implements is silently accepted. A page declaring xmlns:h="jakarta.faces.htm" (one letter short) compiled,
and the response contained the tag exactly as written:
<body><h:outputText value="x"></h:outputText>
In the Development project stage Faces appends a block titled "Unhandled Messages" that warns the page uses an XML name
space with no tag library; MyFaces 4.1.4 produced the same output with a message that names the page, the namespace and the
tag (declares namespace jakarta.faces.htm and uses the tag h:outputText , but no TagLibrary associated to namespace).
In the Production stage there is no warning at all and the browser sees an unknown element.
When a Faces tag appears verbatim in the page source in the browser, look at the namespace declaration first.
Kinds of Tags in a Facelets File
Every element in a Facelets file is one of four things, and which one decides when it takes effect.
| Kind | Examples | What it does |
|---|---|---|
Plain markup and text |
|
Copied to the output (the text and its expressions are compiled into |
Component tags |
|
Create a |
Helper tags |
|
Attach a converter, a validator, a listener, a behavior or an attribute to the component they are nested in. There is no component for the helper itself. |
Tag handlers |
|
Run once while the view is built and leave no component. What they produce is only the components and text they let through. |
The distinction matters for three recurring questions. A rendered attribute exists only on component tags, because only
they stay in the tree to be asked at render time. A c:if can hide a helper tag such as f:ajax or f:validator, which has no
rendered attribute, because it decides whether the helper exists at all. And an expression in a tag handler attribute
(test of c:if, src of ui:include) is evaluated before the submitted values have reached the model on a postback; the
JSTL page shows the measurement. The templating tags and the
components they create are listed one by one in the templating page, and a library of your
own tags is the subject of the tag files and tag libraries page.
h:head and h:body
h:head and h:body render the HTML head and body elements, and they are not interchangeable with the plain
elements. Faces keeps the resources of a view — the style sheets and scripts added by h:outputStylesheet,
h:outputScript, f:ajax (which needs the Faces JavaScript library) and component libraries — and puts them in the
right place when it renders: those with target="head" (the default for style sheets) go into the rendered
h:head, and target="body" ones go to the end of the h:body.
If the page has a plain head instead of h:head, the resource has nowhere to go. A page with this body:
<head><title>Plain head</title></head>
<body><h:outputText value="x"/><h:outputStylesheet name="a.css"/></body>
was rendered with the text x, no style sheet link and, in the Development stage, a warning block at the end saying
that one or more resources have the target head but no head component is defined in the view. The same applies to
a missing h:body when a resource asks for the body. MyFaces 4.1.4 rendered the same page, again without the style sheet,
but without the warning block, so there the symptom is only the missing resource.
Use h:head and h:body in every page that is not a fragment, and put them in the template when a template is used
so that the pages that fill it do not repeat them. In a template client page that supplies only a fragment of a layout
there is no html element at all, which is the topic of the templating page.
f:view and f:metadata
Two tags of the core library describe the view as a whole rather than a part of it.
f:view is the root of the component tree. It is optional in Facelets, because the UIViewRoot always exists, but it is
the place to set properties of the view as a whole: locale (a literal or an expression), contentType, encoding,
renderKitId, beforePhase and afterPhase for phase listeners on this view, contracts for resource library
contracts and transient for a stateless view.
It may wrap the content of the html element, as the example below does.
f:metadata declares the view metadata: parameters read from the request (f:viewParam), actions run on the initial
request (f:viewAction) and system events (f:event). The point of the tag is that the metadata of a page can be read
without building the rest of the page, which is how a bookmarkable URL such as /book.xhtml?isbn=... fills the bean
before the page is rendered. The view parameters page covers them; here it is enough to
know where the tag goes. In a template, the tag cannot be written in the template itself; the portable pattern is a slot
inside the template’s f:view, explained in the templating page.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html"
xmlns:f="jakarta.faces.core"
xmlns:ui="jakarta.faces.facelets">
<f:view locale="de" contentType="text/html">
<f:metadata>
<f:viewParam name="q" value="#{titleBean.q}"/>
</f:metadata>
<h:head><title>New</title></h:head>
<h:body>
#{titleBean.title}, locale #{facesContext.viewRoot.locale}, q=#{titleBean.q}
</h:body>
</f:view>
</html>
A request for /d.xhtml?q=1 rendered
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>New</title></head><body>
Dune & <Messiah>, locale de, q=1
</body>
</html>
so the view’s locale is de and the parameter reached the bean. The f:viewParam writes to a bean property, never to
a read-only expression: pointing it at #{param.q}, which cannot be written, fails with a PropertyNotWritableException
at the position of the tag.
Comments
An XML comment in a Facelets page is not a Facelets comment. It is markup like any other, and in the default processing modes it is passed through to the browser. Worse, expressions inside the comment are still evaluated. This page
<h:body>
<!-- note: #{titleBean.title} -->
<p>Evaluated: #{titleBean.title}</p>
</h:body>
was sent as
<body>
<!-- note: Dune & <Messiah> -->
<p>Evaluated: Dune & <Messiah></p>
</body>
MyFaces 4.1.4 evaluated the expression too, but wrote its result into the comment without escaping it
(<!-- note: Dune & <Messiah> -->), where Mojarra escaped it as shown above. That has two consequences. Every visitor can read your comments in the page
source, and the bean method behind the expression runs even though nothing is visible. In a run of a page with the comment, the paragraph and one more attribute that used the same
expression, the bean getter was called three times, once for each place. If the expression reads a lazily loaded collection or calls a service,
the cost is paid on every request, and if it throws, the page fails because of a comment.
There are two remedies, and they are not equivalent.
-
ui:removedeletes its content when the file is compiled, so nothing in it is evaluated and nothing is sent. Use it for notes to developers and for temporarily disabling a part of a page. Wrapping a comment in it works too:<ui:remove><!-- dev only --></ui:remove>leaves no trace. -
jakarta.faces.FACELETS_SKIP_COMMENTS, set totrueinweb.xml, tells the runtime not to deliver any XML comment to the client. This is a global switch and the right default for a production deployment.
<context-param>
<param-name>jakarta.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
With the parameter set, the book.xhtml comment line above came out as an empty line in the response, and the
Evaluated: paragraph was unchanged. The specification wording is that "any XML comments in the Facelets source page are
not delivered to the client". The specification does not say whether expressions inside a skipped comment are still
evaluated. They were not: with the parameter set, the same page called the getter twice (paragraph and attribute) instead of three
times, on both implementations.
Two cases need care. The old Internet Explorer conditional comments (<!--[if IE]> ... <![endif]-->) are comments and
are dropped by the switch; they are historical and no current browser needs them. Without the switch Mojarra 4.1.17 did not
deliver such a comment intact: the evaluated text was escaped, giving <!--[if IE]> ie <![endif]-->, while MyFaces
delivered it unchanged. And a comment inside an inline script element is not safe from the switch either: with the parameter
set, <script>/* <!-- in script --> */ var a = 1;</script> was sent as <script>/* */ var a = 1;</script> by both
implementations, so the switch also removes the <!-- --> pairs that old scripts used to hide code from browsers.
Escaping
Three kinds of escaping meet in a Facelets page, and they are applied at different times.
| Where | Rule | Example (observed on Mojarra 4.1.17) |
|---|---|---|
In the XHTML source |
The file is XML: |
|
In the value of an EL expression |
The result is HTML-escaped when written, in template text, in attribute values and by |
|
An expression that must stay literal |
Put a backslash before the opening of the expression, or compute the characters |
|
Three details are worth knowing.
Output is escaped by default, and escape="false" is how you lose that. This page
<h:outputText value="<b>bold</b>" escape="false"/>
is sent as <b>bold</b>, which is the intended effect for text you control. With a value that comes from a user, the same
attribute is a stored cross-site scripting hole. Escape-free output belongs only on markup produced by the application,
and a user-supplied rich text must pass through a sanitiser first.
A CDATA section survives, but its content is escaped. In both the html5 and xhtml modes the start and end tags of a
CDATA section are passed through, and the text inside is escaped like any other text, so
<p>Cdata: <![CDATA[ a < b ]]></p>
is sent as <p>Cdata: <![CDATA[ a < b ]]></p>. A browser parsing the output as HTML does not treat <![CDATA[ as a
CDATA section, so the markers appear as a (bogus) comment or as text depending on the browser. Do not use CDATA to put
script or style into a page that is served as HTML. Move the script to a file and load it with h:outputScript, which is also
what the content security policies of a modern application require, or write < where a < is needed.
The jspx mode does not escape inline text. This is the only mode of the four in which the text is passed through
raw, as the table in The XML declaration and the doctype shows, and only for files you have mapped to that mode on purpose.
Components Are Not Re-Created on Redisplay
A Facelets file is run again every time its view is built, and a postback builds the view again (the Restore View
phase runs the same tag handlers, then applies the saved state). The Faces specification says what the component tags do
when the view they build into already contains components, in the section on deleting components on redisplay:
-
A component tag creates a new component only when the parent has no child with the component identifier it would use; if such a child is already in the tree, "take no additional action" (section 9.2.3). That is why a postback does not produce a second copy of every component, and why the state saved with the first display can be applied to the same components.
-
A component tag also deletes the child components and facets that are in the tree but are not rendered on this display of the page (section 9.2.4). The specification’s own example is a component tag nested in a
c:ifwhose condition is true on the first display and false on the redisplay: the component created on the first display is removed from the tree.
The first rule keeps the tree stable; the second is the reason a build-time condition and the rendered attribute are not
interchangeable. The measurement uses a view-scoped bean with a flag and two outputs, one under c:if and one with
rendered, and prints the client ids of the components that are in the tree:
@Named("demo")
@ViewScoped
public class Demo implements Serializable {
private boolean visible = true;
public boolean isVisible() { return visible; }
public void hide() { visible = false; }
public void apply() { }
public String getTree() {
StringBuilder sb = new StringBuilder();
collect(FacesContext.getCurrentInstance().getViewRoot(), sb);
return sb.toString();
}
private void collect(UIComponent c, StringBuilder sb) {
if (c.getId() != null && !c.getId().startsWith("j_")) sb.append(c.getClientId()).append(' ');
for (UIComponent k : c.getChildren()) collect(k, sb);
}
}
<h:form id="f">
<c:if test="#{demo.visible}"><h:outputText id="byTag" value="by c:if"/></c:if>
<h:outputText id="byRendered" value="by rendered" rendered="#{demo.visible}"/>
<h:commandButton id="hide" value="Hide" action="#{demo.hide}"/>
<h:commandButton id="noop" value="Noop" action="#{demo.apply}"/>
</h:form>
<p>tree=[#{demo.tree}]</p>
The tree paragraph on the initial GET, after a postback of the noop button, after hide and after another noop:
GET tree=[f f:byTag f:byRendered f:hide f:noop ]
POST noop tree=[f f:byTag f:byRendered f:hide f:noop ]
POST hide tree=[f f:byRendered f:hide f:noop ] (1)
POST noop tree=[f f:byRendered f:hide f:noop ] (2)
| 1 | f:byTag is gone from the tree. The page also stopped printing by c:if, and by rendered too, because visible is now false. |
| 2 | It does not come back by itself; a later build creates it only if its c:if condition is true again. |
The two implementations gave identical results. f:byRendered stayed in the tree throughout and was
skipped by the renderer; f:byTag was deleted. For a plain output the difference is invisible. For an input
it is not: a deleted input loses its submitted value, its validation state and the listeners and Ajax behaviors that were
attached to it, and an Ajax request that names it in render or execute can no longer find it. So, for anything that the
user can show and hide, use rendered, or ui:fragment with rendered, and keep c:if for decisions that do not
change while the view lives (see the decision table).
Context Parameters That Affect the Page
The parameters below are specified for every implementation. They are set as context-param entries in web.xml.
| Parameter | Effect |
|---|---|
|
|
|
Interval in seconds at which the compiler checks the file for changes; |
|
Response buffer size when the |
|
Semicolon separated list of paths, starting with a slash, of Facelets tag library files to load (taglib XML) |
|
Semicolon separated list of |
|
The suffix or suffixes of Facelets pages (File extension and view identifier) |
|
Patterns of pages forced to be processed as Facelets whatever their extension |
During development on Mojarra 4.1.17, setting jakarta.faces.FACELETS_REFRESH_PERIOD to 1 was needed to see an edit
of an XHTML file without a restart; the project setup page explains the edit-and-reload loop.
Mojarra and MyFaces
The examples of this page were run on Mojarra 4.1.17 and, except for the items named under the table, on MyFaces 4.1.4. Most behaviour is
identical: the XHTML rules and error messages, the doctype and processing modes, the old and new namespaces, f:view, escaping and
comment handling all matched. The differences found:
| Situation | Mojarra 4.1.17 | MyFaces 4.1.4 |
|---|---|---|
Parse error page title |
|
The same message, with the view id shown as |
Plain |
Warning block "Unhandled Messages" in the Development stage |
No warning; the resource is just missing |
Wrong namespace in the Development stage |
|
|
Expression result inside an XML comment |
HTML-escaped |
Written unescaped |
IE conditional comment |
Escaped text inside the comment, delivered altered |
Delivered unchanged |
|
|
|
Development stage, extra output |
Only the warning blocks |
An empty |
Not repeated on MyFaces: the context-parameter table, the jspx processing mode and the behaviour of the
FACELETS_BUFFER_SIZE and FACELETS_DECORATORS parameters, whose descriptions come from the specification.
Source: the scratch runs described at the top of this page.
Where the Books Are Outdated
Three of the books consulted for this section discuss Facelets as a view technology. They are useful for the concepts and wrong in the details of every file you would type from them.
-
Aranda and Wadia, Facelets Essentials (2008). The book covers Facelets 1.x, a separate library added to JSF 1.1 or 1.2. In that setup Facelets was a JAR in
WEB-INF/lib, installed by registering thecom.sun.facelets.FaceletViewHandlerclass infaces-config.xml, configured through parameters namedfacelets.*(facelets.DEVELOPMENT,facelets.SKIP_COMMENTS,facelets.REFRESH_PERIOD,facelets.LIBRARIES) and imported from thehttp://java.sun.com/jsf/faceletsnamespace. All of that was absorbed into the platform in JSF 2.0: Facelets is built in and is the default VDL, noViewHandleris installed, the parameters arejakarta.faces.FACELETS_*(they werejavax.faces.FACELETS_*before Faces 3.0), the development switch is the project stage, and the namespaces are the URNs of Namespaces. What has not changed is the case the book makes for why Facelets replaced JSP — compile to a tree, unified EL in template text, templating — which matches the specification table in Facelets Compared with Jakarta Server Pages. Itsjsfcattribute, the precursor of pass-through markup, is replaced by the pass-through attributes and elements covered by the HTML5-friendly markup page. -
Burns and Schalk, JavaServer Faces 2.0: The Complete Reference (2010). Written by the specification lead, its account of Facelets as the VDL and of the XHTML rules is still accurate. Its examples use the
java.sun.comnamespaces and thejavax.facespackages, and JSP views are still discussed as an alternative; JSP is gone in Faces 4.0. -
Leonard, JSF 2.0 Cookbook (2010). Its Facelets recipes remain valid as ideas, but they use the
java.sun.comnamespaces andjavax.facesclasses, and some rely on RichFaces and Tomahawk tags that no longer exist. Use the current namespaces and PrimeFaces or OmniFaces components for the same ideas.
On a discrepancy the official documentation linked on this page wins.
Checklist of Common Mistakes
-
The page is HTML, not XML. An unclosed
<br>,<img>or<input>, an unquoted attribute or a bare&in a URL such as?a=1&b=2: write<br/>, quote every attribute value and write?a=1&b=2. -
A Faces tag shows up in the page source. The prefix is bound to a namespace that has no tag library, usually a typo in the URN or the use of an old URI in a page that was copied between applications.
-
Expressions in comments. They run. Use
ui:removeorjakarta.faces.FACELETS_SKIP_COMMENTS. -
A plain
headorbody. Resources have no target. Useh:headandh:body. -
escape="false"on user data. A cross-site scripting hole. -
A CDATA section used to hold a script. Escaped, and not a CDATA section in HTML. Use
h:outputScript. -
A named entity such as
©with the HTML5 doctype. A parse error. Use the numeric reference or the character. -
A
javax.facesorJSPpage copied from a book. There is nojsp:include, no.jspview and nojavaxnamespace in Faces 4.x. -
f:viewParampointing at a read-only expression. It needs a writable property. -
Showing and hiding an input with
c:if. The component is deleted when the condition turns false and its state goes with it (Components Are Not Re-Created on Redisplay). Userendered. -
Expecting the same comment output everywhere. MyFaces writes the evaluated expression of a comment unescaped and Mojarra escapes it (Mojarra and MyFaces); keep expressions out of comments.
-
FACELETS_SKIP_COMMENTSand inline scripts. The switch also removes the<!-- -->inside an inlinescript.
What Changed Between Versions
| Version | Change relevant to Facelets views |
|---|---|
JSF 2.0 |
Facelets becomes part of the specification and the default VDL; JSP is deprecated; |
JSF 2.2 |
The |
Faces 3.0 |
The package rename from |
Faces 4.0 |
JSP views removed; the URN namespaces |
Faces 4.1 |
Same page model; full state saving is deprecated, which does not change how pages are written |
Faces 5.0 (upcoming, milestone |
Support for the old |
References
-
Jakarta Faces 4.1 Specification (10 Facelets and its use in Web Applications: 10.1.1 Differences between Jakarta Server Pages and Facelets, 10.3 XHTML Specification, 10.3.1.1 DOCTYPE and XML Declaration, 10.4 Standard Facelet Tag Libraries; A.1.1.1 The facelets-processing element; the Facelets context parameters)
-
Jakarta Faces 4.1 VDL documentation (the h:, f:, ui:, cc:, jstl-core: and jstl-fn: tag libraries) and f:view
-
Jakarta Faces 4.1 Specification, 9.2 Using UIComponent Tags (9.2.3 Creating Components and Overriding Attributes, 9.2.4 Deleting Components on Redisplay)
-
Jakarta EE Tutorial: What Is Facelets? (the tutorial describes Faces 4.0; the namespace table is current)
-
Facelets Essentials: Guide to JavaServer Faces View Definition Framework (Aranda and Wadia, Apress, 2008)
-
JavaServer Faces 2.0: The Complete Reference (Burns and Schalk, McGraw-Hill, 2010)
-
JSF 2.0 Cookbook (Leonard, Packt, 2010)