By the end of this week you will be able to:
Every web page ultimately reaches the browser as HTML. But how that HTML is produced varies greatly:
You write the HTML directly. The file is sent to the browser as-is. Data never changes unless you edit the file.
The server builds the HTML page dynamically — data from a database is injected before sending to the browser. This is what Jakarta Faces does.
The browser downloads a JavaScript app that builds the page itself. The server only provides raw data (via APIs). Examples: React, Angular, Vue.
| Criterion | Raw HTML / JSP | Jakarta Faces (Facelets) | React / Angular (SPA) |
|---|---|---|---|
| Who builds the page? | Developer writes HTML manually | JF framework generates HTML from components | Browser JavaScript builds it |
| Java integration | Manual (JSP scriptlets — messy) | Native — beans connect directly | Via REST API calls |
| Reusable components? | Limited (manual copy-paste) | Yes — rich component library | Yes — component ecosystem |
| Templating / layouts? | No standard approach | Built-in with Facelets templates | Component composition |
| Best suited for | Simple, small sites | Enterprise Java applications | Highly interactive modern UIs |
The older approach to Java web pages was JSP (JavaServer Pages). Facelets replaced it as the standard view technology for Jakarta Faces.
Think of it like this: HTML gives you a set of building blocks (<input>, <button>). Facelets PDL extends that vocabulary with smarter, Java-aware versions:
| Plain HTML | Facelets PDL equivalent | What makes it smarter |
|---|---|---|
<input type="text"> |
<h:inputText value="#{bean.name}"/> |
Automatically binds to a Java property; handles validation |
<button>Submit</button> |
<h:commandButton action="#{bean.save}"/> |
Calls a Java method; manages navigation outcome |
<table>...</table> |
<h:dataTable value="#{bean.items}"/> |
Iterates a Java list automatically; generates rows |
<span>text</span> |
<h:outputText value="#{bean.total}"/> |
Reads a Java value; supports formatters |
Facelets pages are valid XML files. To use JF tags, you declare namespaces in the opening <html> tag — these tell the server which tags come from which library.
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html" ← HTML components (inputs, forms, tables)
xmlns:f="jakarta.faces.core" ← Core tags (validators, converters, events)
xmlns:ui="jakarta.faces.facelets" ← Templating (define, insert, include)
xmlns:c="jakarta.tags.core" ← JSTL core (if/forEach logic)
>
The prefix (h:, f:, ui:) is like a first name that tells JF which library owns the tag:
http://xmlns.jcp.org/jsf/html. Modern Jakarta EE uses the short form jakarta.faces.html. Using the wrong one is a common student error — always use the jakarta. prefix.
The most powerful feature of Facelets PDL is its templating system. Define a master layout once; every page just fills in the slots.
layout.xhtml)The ui:insert is the slot — a named hole that child pages fill in.
home.xhtml)<ui:composition
template="/layout.xhtml">
<ui:define name="content">
<!-- This fills the slot -->
<h2>Welcome Home!</h2>
<p>Today's date is
<h:outputText
value="#{bean.today}"/>
</p>
</ui:define>
</ui:composition>
ui:define provides the content for the named slot.
xmlns:h="http://xmlns.jcp.org/jsf/html" in their Facelets page. What is the likely problem?Jakarta Faces components are grouped by the namespace prefix that identifies them:
| Prefix | Library | Purpose | Examples |
|---|---|---|---|
h: |
HTML Components | Standard UI widgets rendered as HTML | Forms, inputs, buttons, tables, links |
f: |
Core Components | Non-visual: validators, converters, events | Validate email, format dates, AJAX |
ui: |
Facelets Tags | Templating and composition | Templates, fragments, includes, repeat |
c: |
JSTL Core | Flow control logic in the page | if-else conditions, forEach loops |
h: as your visible furniture (what users interact with), f: as the wiring behind the walls (invisible rules and behaviour), and ui: as the architectural blueprint (page structure).
h: Libraryh: components<h:form>, never a plain HTML <form>. The JF form adds state-tracking data that makes the framework work.
<!-- A complete login form -->
<h:form>
<h:outputLabel
for="email"
value="Email:"/>
<h:inputText
id="email"
value="#{loginBean.email}"/>
<h:outputLabel
for="pw"
value="Password:"/>
<h:inputSecret
id="pw"
value="#{loginBean.password}"/>
<h:commandButton
value="Login"
action="#{loginBean.login}"/>
<h:messages globalOnly="true"/>
</h:form>
f: LibraryCore components are non-visual — they attach behaviour to other components. They are always nested inside an h: component.
Automatically check user input before it reaches your Java code:
<h:inputText
value="#{bean.age}">
<f:validateLongRange
minimum="18"
maximum="120"/>
</h:inputText>
<h:inputText
value="#{bean.code}">
<f:validateLength
minimum="6"
maximum="10"/>
</h:inputText>
Transform data between the String the browser sends and the Java type your bean expects:
<h:inputText
value="#{bean.salary}">
<f:convertNumber
minFractionDigits="2"
maxFractionDigits="2"/>
</h:inputText>
<h:outputText
value="#{bean.birthDate}">
<f:convertDateTime
pattern="dd/MM/yyyy"/>
</h:outputText>
double field — JF would throw a conversion error.
h:dataTableh:dataTable is one of the most powerful JF components — it automatically generates an HTML table from a Java List.
value — points to your Java list (#{bean.products})var — names the variable for each row item (p)h:column is one table column<!-- In the Facelet page -->
<h:dataTable
value="#{productBean.products}"
var="p"
border="1">
<h:column>
<f:facet name="header">Name</f:facet>
<h:outputText
value="#{p.name}"/>
</h:column>
<h:column>
<f:facet name="header">Price</f:facet>
<h:outputText
value="#{p.price}">
<f:convertNumber
type="currency"/>
</h:outputText>
</h:column>
</h:dataTable>
Sometimes you want to show or hide parts of a page, or repeat a block. JF gives you two approaches:
rendered attributeEvery JF component has a rendered attribute. Set it to a boolean EL expression — the component only appears in the HTML when it evaluates to true.
<!-- Only show if logged in -->
<h:panelGroup
rendered="#{userBean.loggedIn}">
<h:outputText
value="Welcome back!"/>
</h:panelGroup>
<!-- Show error only if present -->
<h:outputText
value="#{bean.error}"
rendered="#{not empty bean.error}"
style="color:red"/>
ui:repeatFor repeating a block that isn't a table, ui:repeat is the Facelets way:
<!-- List of messages -->
<ul>
<ui:repeat
value="#{inbox.messages}"
var="msg">
<li>
<h:outputText
value="#{msg.subject}"/>
</li>
</ui:repeat>
</ul>
h:dataTable when you want an HTML <table>. Use ui:repeat when you want to repeat any arbitrary block of HTML.
When validation fails, JF generates FacesMessages. You must add a component to the page to display them — they don't appear automatically.
| Component | What it displays |
|---|---|
<h:message for="fieldId"/> |
Error message for one specific input field |
<h:messages/> |
All messages on the page in a list |
<h:messages globalOnly="true"/> |
Only messages not tied to a specific field (e.g., "Login failed") |
<h:inputText
id="age"
value="#{bean.age}">
<f:validateLongRange
minimum="18" maximum="99"/>
</h:inputText>
<!-- Shows error just for this field -->
<h:message for="age" style="color:red"/>
h:, f:, ui:ui:insert / ui:define)h:dataTable, includes a form to add new entries, and validates all inputs before saving.