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.

flowchart LR file["book.xhtml
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:if that is decided at build time behaves differently from an attribute such as rendered that 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.

TitleBean.java, a request scoped bean with one read-only property
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.

src/main/webapp/book.xhtml
<?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:

  1. 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.

  2. The DOCTYPE is covered in The XML declaration and the doctype. <!DOCTYPE html> is the HTML5 form and the one to use.

  3. The root element is the ordinary html element in the XHTML namespace. Everything that is not in a Faces namespace is copied through to the output.

  4. Each xmlns: attribute binds a prefix to a tag library (Namespaces). A prefix that is used but not bound is a parse error.

  5. h:head and h:body replace head and body and are what makes Faces able to place scripts and style sheets (h:head and h:body).

  6. #{titleBean.title} in the middle of the paragraph is template text containing an expression, and the h:outputText below it prints the same value.

Running the page on Mojarra 4.1.17 produces this response:

Response for book.xhtml, exactly as received
<?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 &amp; &lt;Messiah&gt;</p>Dune &amp; &lt;Messiah&gt;</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 &amp; &lt;Messiah&gt;: 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_SUFFIX sets the suffix or the list of suffixes of files that contain Facelets markup.

  • jakarta.faces.FACELETS_VIEW_MAPPINGS is 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 &amp; and &lt;. The classic casualty is the HTML void element. This page is valid HTML5 and is rejected by Facelets:

A page that is HTML but not XML
<!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 <!DOCTYPE html>

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:

A page with the 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:

WEB-INF/faces-config.xml, keeping the XHTML doctype in the response
<?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: &amp;, &lt;, &gt;, &quot; and &apos;. Every other named HTML entity, &copy; or &nbsp; 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 &copy; 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: &#169;, &#xA9; and &#160; 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 &copy;, 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)

jakarta.faces.html

h:

Component tags for the standard HTML render kit: h:head, h:body, h:form, h:inputText, h:commandButton, …​

http://xmlns.jcp.org/jsf/html and, before that, http://java.sun.com/jsf/html

jakarta.faces.core

f:

Render kit independent tags: f:view, f:metadata, f:ajax, f:converter, f:validator, f:attribute, …​

http://xmlns.jcp.org/jsf/core, http://java.sun.com/jsf/core

jakarta.faces.facelets

ui:

Templating: ui:composition, ui:define, ui:insert, ui:include, ui:repeat, ui:remove, …​

http://xmlns.jcp.org/jsf/facelets, http://java.sun.com/jsf/facelets

jakarta.faces.composite

cc:

Composite component declaration: cc:interface, cc:implementation, …​

http://xmlns.jcp.org/jsf/composite, http://java.sun.com/jsf/composite

jakarta.faces.passthrough

p:

Pass-through attributes for HTML5 markup (p:type, p:placeholder)

http://xmlns.jcp.org/jsf/passthrough

jakarta.faces

faces: (formerly jsf:)

Pass-through elements: turn a plain HTML element into a component (faces:id, faces:value)

http://xmlns.jcp.org/jsf

jakarta.tags.core

c:

The subset of the Jakarta Tags (JSTL) core library that Facelets exposes: c:if, c:forEach, c:set, …​

http://xmlns.jcp.org/jsp/jstl/core, http://java.sun.com/jsp/jstl/core

jakarta.tags.functions

fn:

The whole Jakarta Tags function library: fn:toUpperCase, fn:length, …​

http://xmlns.jcp.org/jsp/jstl/functions, http://java.sun.com/jsp/jstl/functions

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

<p>, <div>, Title: #{bean.title}

Copied to the output (the text and its expressions are compiled into UIInstructions; see Expressions in text become components).

Component tags

h:inputText, h:dataTable, h:panelGrid, ui:repeat, f:view, composite components

Create a UIComponent and add it to the tree while the view is built. Their attributes, such as value and rendered, are evaluated while the view is processed and rendered.

Helper tags

f:convertNumber, f:validateLength, f:actionListener, f:event, f:ajax, f:attribute

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

c:if, c:forEach, c:set, ui:include, ui:define, ui:decorate, ui:param, ui:remove

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.

A page with f:view and f:metadata, written with the current namespaces
<!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 &amp; &lt;Messiah&gt;, 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 &amp; &lt;Messiah&gt; -->
  <p>Evaluated: Dune &amp; &lt;Messiah&gt;</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:remove deletes 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 to true in web.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.

web.xml, dropping every XML comment from the response
<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]&gt; ie &lt;![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: & and < must be &amp; and &lt;

a &amp; b &lt;tag&gt; is sent as a &amp; b &lt;tag&gt;

In the value of an EL expression

The result is HTML-escaped when written, in template text, in attribute values and by h:outputText

#{titleBean.title} gives Dune &amp; &lt;Messiah&gt;, also inside title="Tip: #{titleBean.title}"

An expression that must stay literal

Put a backslash before the opening of the expression, or compute the characters

\#{titleBean.title} is sent as #{titleBean.title}; #{'#{'}titleBean.title} gives the same

Three details are worth knowing.

Output is escaped by default, and escape="false" is how you lose that. This page

<h:outputText value="&lt;b&gt;bold&lt;/b&gt;" 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 &lt; 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 &lt; 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:if whose 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:

Demo.java, the parts used (a view scoped bean, excerpt)
@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);
    }
}
redisplay.xhtml
<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

jakarta.faces.FACELETS_SKIP_COMMENTS

true stops XML comments from reaching the client (Comments)

jakarta.faces.FACELETS_REFRESH_PERIOD

Interval in seconds at which the compiler checks the file for changes; -1 never checks. When not set, the default depends on the project stage: in Production the runtime acts as if it were -1, in any other stage as if it were 0

jakarta.faces.FACELETS_BUFFER_SIZE

Response buffer size when the ResponseWriter is generated; the default is 1024 and -1 leaves the buffer alone. Raise it in development so that an error is not shown after part of the page has already been sent

jakarta.faces.FACELETS_LIBRARIES

Semicolon separated list of paths, starting with a slash, of Facelets tag library files to load (taglib XML)

jakarta.faces.FACELETS_DECORATORS

Semicolon separated list of jakarta.faces.view.facelets.TagDecorator class names

jakarta.faces.FACELETS_SUFFIX

The suffix or suffixes of Facelets pages (File extension and view identifier)

jakarta.faces.FACELETS_VIEW_MAPPINGS

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

Error Parsing /b.xhtml: Error Traced[line: 7] ...

The same message, with the view id shown as /viewMetadata/b.xhtml

Plain head with a resource of target head

Warning block "Unhandled Messages" in the Development stage

No warning; the resource is just missing

Wrong namespace in the Development stage

This page calls for XML namespace jakarta.faces.htm declared with prefix h but no taglibrary exists for that namespace.

The page /x.xhtml declares namespace jakarta.faces.htm and uses the tag h:outputText , but no TagLibrary associated to namespace.

Expression result inside an XML comment

HTML-escaped

Written unescaped

IE conditional comment

Escaped text inside the comment, delivered altered

Delivered unchanged

f:viewParam bound to #{param.q}

PropertyNotWritableException

ContextAwarePropertyNotWritableException wrapping the same exception, Cannot set ServletRequest Parameter

Development stage, extra output

Only the warning blocks

An empty div with id jakarta_faces_developmentstage_messages before </body>

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 the com.sun.facelets.FaceletViewHandler class in faces-config.xml, configured through parameters named facelets.* (facelets.DEVELOPMENT, facelets.SKIP_COMMENTS, facelets.REFRESH_PERIOD, facelets.LIBRARIES) and imported from the http://java.sun.com/jsf/facelets namespace. All of that was absorbed into the platform in JSF 2.0: Facelets is built in and is the default VDL, no ViewHandler is installed, the parameters are jakarta.faces.FACELETS_* (they were javax.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. Its jsfc attribute, 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.com namespaces and the javax.faces packages, 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.com namespaces and javax.faces classes, 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&amp;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:remove or jakarta.faces.FACELETS_SKIP_COMMENTS.

  • A plain head or body. Resources have no target. Use h:head and h: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 &copy; with the HTML5 doctype. A parse error. Use the numeric reference or the character.

  • A javax.faces or JSP page copied from a book. There is no jsp:include, no .jsp view and no javax namespace in Faces 4.x.

  • f:viewParam pointing 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). Use rendered.

  • 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_COMMENTS and inline scripts. The switch also removes the <!-- --> inside an inline script.

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; f:metadata and f:viewParam; jsf.js

JSF 2.2

The http://xmlns.jcp.org/jsf/* namespaces; HTML5 friendly markup (pass-through attributes and elements)

Faces 3.0

The package rename from javax.faces to jakarta.faces, which also renames the context parameters (javax.faces.FACELETS_SKIP_COMMENTS becomes jakarta.faces.FACELETS_SKIP_COMMENTS)

Faces 4.0

JSP views removed; the URN namespaces jakarta.faces.* and jakarta.tags.* introduced, the old URIs still accepted; faces.js replaces jsf.js

Faces 4.1

Same page model; full state saving is deprecated, which does not change how pages are written

Faces 5.0 (upcoming, milestone 5.0.0-M9)

Support for the old xmlns.jcp.org and java.sun.com namespaces is planned to be removed (issue 2105 on the milestone page); the milestone page also lists a rewording of the requirements for EL expressions written in a page outside components (issue 1244). Not described here as current behaviour

References