Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Jakarta Faces

Jakarta Faces (formerly JSF) is a technology for server-side HTML pages built from components. A page is described in an XHTML file, data and actions come from a bean, and the server builds the HTML, accepts forms and validates fields. New interfaces are more often built in JavaScript on top of a REST API today, but many enterprise systems are written in Faces and still need maintenance. This lesson introduces the basics.

Enabling Faces

Faces is enabled with the @FacesConfig annotation on any bean of the application:

// src/main/java/com/example/blog/FacesActivator.java
package com.example.blog;
 
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.faces.annotation.FacesConfig;
 
@FacesConfig
@ApplicationScoped
public class FacesActivator {
}

The page bean

A page needs an object to take data from. The @Named annotation makes the bean available on the page under the name postsPage — the class name starting with a lowercase letter:

// src/main/java/com/example/blog/PostsPage.java
package com.example.blog;
 
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.util.List;
 
@Named
@RequestScoped
public class PostsPage {
 
    @Inject
    PostRepository repository;
 
    @Inject
    PostService service;
 
    private String title;
    private String body;
 
    public List<Post> getPosts() {
        return repository.findAll();
    }
 
    public String publish() {
        service.publish(new NewPost(title, body));
        return "posts?faces-redirect=true";
    }
 
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getBody() { return body; }
    public void setBody(String body) { this.body = body; }
}

Form fields are bound to a getter–setter pair. The publish method is called by a button and returns where to go next: faces-redirect=true asks the browser to load the page again so that a refresh does not submit the form twice.

The page

Create the file src/main/webapp/posts.xhtml:

<!-- src/main/webapp/posts.xhtml -->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="jakarta.faces.html"
      xmlns:ui="jakarta.faces.facelets">
<h:head>
    <title>Blog posts</title>
</h:head>
<h:body>
    <h1>Blog posts</h1>
 
    <ul>
        <ui:repeat value="#{postsPage.posts}" var="post">
            <li>#{post.title} (#{post.createdAt})</li>
        </ui:repeat>
    </ul>
 
    <h2>New post</h2>
    <h:form id="postForm">
        <h:outputLabel for="title" value="Title" />
        <h:inputText id="title" value="#{postsPage.title}" required="true" />
 
        <h:outputLabel for="body" value="Text" />
        <h:inputTextarea id="body" value="#{postsPage.body}" />
 
        <h:commandButton value="Publish" action="#{postsPage.publish}" />
        <h:messages />
    </h:form>
</h:body>
</html>

#{...} expressions are the Expression Language: #{postsPage.posts} calls getPosts(), #{postsPage.publish} calls the publish method. The h: and ui: prefixes bring in component libraries.

The result

Open http://localhost:8080/blog/posts.xhtml. The server returns plain HTML — a list and a form:

<ul>
        <li>Hello, Jakarta EE (2026-10-01)</li>
        <li>Servlets and filters (2026-10-02)</li>
</ul>
<h2>New post</h2><form id="postForm" name="postForm" method="post" action="/blog/posts.xhtml" ...>

Fill in the form and press "Publish" — the post is saved through the same PostService as in the REST API and shows up in the list. If you leave the title empty, required="true" stops the form, and h:messages shows postForm:title: Validation Error: Value is required.

Main components

ComponentWhat it renders
h:formA form
h:inputText, h:inputTextareaAn input field and a multi-line field
h:commandButtonA button that calls a bean method
h:outputTextText with HTML escaping
h:dataTableA table from a list
h:messagesError messages
ui:repeatRepeats markup for each element

Common mistake

Creating an .xhtml page but not enabling Faces. Without @FacesConfig (or a WEB-INF/faces-config.xml file) the server serves the file as is, and the browser gets the source text with h:body tags and #{postsPage.posts} expressions instead of the list of posts.

Thanks for reading!

Donate to the site — we work hard for you.