1. JSF Lifecycle (6 Phases)

Every request generally passes through six phases. If a conversion/validation error occurs, or if responseComplete() or renderResponse() is called, JSF jumps directly to Render Response.

1Restore View
2Apply Request Values
3Process Validations
4Update Model Values
5Invoke Application
6Render Response
Direct jump to Render Response: If a conversion or validation error occurs in phases 2–5 (or FacesContext.responseComplete()/renderResponse() is called), JSF skips the remaining phases and renders immediately with the error messages.
immediate="true": Components with this attribute (e.g. a "Cancel" button) perform conversion/validation already in phase 2 (Apply Request Values) instead of phase 3 – this lets you deliberately bypass validation.

Brief description of the phases

PhaseWhat happens
1. Restore ViewThe component tree of the requested view is built from the saved state (or newly created). On an initial request a new view is created; on a postback, the saved view state is restored.
2. Apply Request ValuesThe values submitted via HTTP are applied to the respective UIComponents as a "local value" (submitted value), without yet writing them into bean properties.
3. Process ValidationsFor each component, the submitted string is converted into a Java object via a converter and then checked against the registered validators. On errors, FacesMessages are generated and JSF jumps straight to Render Response.
4. Update Model ValuesThe converted and validated values are actually written into the bean properties bound via EL (the setters are called).
5. Invoke ApplicationAction methods (e.g. a button click) and action listeners are executed; the result determines, via navigation, the next view to display.
6. Render ResponseThe (possibly new) component tree is returned to the client as HTML; the view state is saved for the next postback.

2. Important Annotations

Annotation Origin Purpose
@NamedCDIMakes a class addressable as a CDI bean via Expression Language (#{beanName}). The standard replacement for the old @ManagedBean.
@ManagedBeanJSF (legacy)The old, JSF-native bean management (javax.faces.bean). Deprecated since JSF 2.3 – today use @Named + CDI instead.
@RequestScopedCDIThe bean instance lives only for the duration of a single HTTP request.
@ViewScopedCDI (jakarta.faces.view)The bean lives as long as the current view is displayed – it survives multiple Ajax postbacks and is destroyed when navigating to another page.
@SessionScopedCDIOne instance per user session, until logout/session timeout, e.g. for login information, language settings, or a shopping cart.
@ApplicationScopedCDIExactly one instance for the entire application, shared by all users (e.g. for caches/configuration).
@ConversationScopedCDIThe bean lives across multiple views until the conversation is explicitly ended (typical for multi-step wizards).
@InjectCDIInjects another bean or dependency into a field, constructor, or setter.
@ManagedPropertyJSF (legacy)Injects values or other managed beans into a legacy JSF bean. Replaced by @Inject.
@FacesConverterJSFMarks a class as a custom converter that converts between the display string and a Java object.
@FacesValidatorJSFMarks a class as a custom validator for checking input values.
@FacesComponentJSFDefines a custom UIComponent class to extend the component tree.
@FacesRendererJSFDefines a custom renderer that determines how a component is rendered as HTML.
@FacesBehaviorJSFDefines a custom client behavior, e.g. to extend <f:ajax>.

Note: In newer Jakarta EE versions the packages live under jakarta.* instead of javax.* – the concepts and names of the annotations stay the same.

3. Composite Component – Code Example

Composite components are custom, reusable Facelets tags that are placed in the /resources folder.

/resources/components/labeledInput.xhtml (definition)
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:composite="http://xmlns.jcp.org/jsf/composite"
      xmlns:h="http://xmlns.jcp.org/jsf/html">

<composite:interface>
    <composite:attribute name="label" required="true" />
    <composite:attribute name="value" required="true" />
    <composite:attribute name="required" default="false" />
</composite:interface>

<composite:implementation>
    <h:outputLabel value="#{cc.attrs.label}" for="input" />
    <h:inputText id="input"
                 value="#{cc.attrs.value}"
                 required="#{cc.attrs.required}" />
    <h:message for="input" style="color:red" />
</composite:implementation>
</html>
Usage in any page
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:my="http://xmlns.jcp.org/jsf/composite/components">

    <my:labeledInput label="Name" value="#{userBean.name}" required="true" />

</html>

cc.attrs.xxx accesses, from within the component, the attributes declared via composite:attribute. The namespace path jsf/composite/<foldername> is derived automatically from the subfolder in /resources.

4. CDI Scopes in Detail

Lifetime: 1 request

@RequestScoped

A new instance on every HTTP request. The simplest, stateless variant – suitable for simple forms/list views without state across multiple requests.

Lifetime: current view

@ViewScoped

Persists as long as the user stays on the same page – even across multiple Ajax postbacks. Destroyed when navigating to another view. Ideal for forms with multiple Ajax interactions (e.g. selecting a table row, multi-step input forms on one page).

Lifetime: HTTP session

@SessionScoped

One instance per user session, until logout/session timeout. Typical for login information, language settings, or a shopping cart.

Lifetime: entire application

@ApplicationScoped

Exactly one instance, shared by all users and sessions. For global configuration, reference data, or caches. Must be thread-safe, since it's used concurrently by multiple users.

Lifetime: explicit conversation

@ConversationScoped

Must be actively started (conversation.begin()) and ended (conversation.end()). It then survives across multiple views – useful for multi-page wizards where @ViewScoped would be too short-lived and @SessionScoped too long-lived.

Lifetime: Dependent (default)

@Dependent

The CDI default scope if none is specified. A new instance is created on every injection, with its lifecycle tied to the injecting object – no shared state.

Practical tip: For classic JSF pages with forms and Ajax, @ViewScoped combined with @Named is by far the most common case, since the form state must be preserved across multiple Ajax requests without burdening the entire session.

5. JSF Scopes (native)

Before CDI, JSF had its own scope annotations under javax.faces.bean.* (JSF managed beans). Technically, these scopes are simply mapped onto the standard servlet objects. A special case is the flash scope, which exists only in JSF – it doesn't exist in CDI.

ScopeStorage location (technical)Special characteristic
@RequestScopedAttributes of HttpServletRequestIdentical to the CDI counterpart, just a different package origin (javax.faces.bean instead of CDI).
@ViewScopedUIViewRoot.getViewMap() – part of the component treeOriginally JSF-specific; was later made CDI-compatible as javax.faces.view.ViewScoped.
@SessionScopedAttributes of HttpSessionIdentical to the CDI counterpart.
@ApplicationScopedAttributes of ServletContextIdentical to the CDI counterpart.
@NoneScopeThe bean is not cached; every EL access creates a new instance (no CDI equivalent needed, since @Dependent behaves similarly).
Flash scopeExternalContext.getFlash() – a cookie-based, short-lived attributeSurvives exactly one redirect. Ideal for still showing a success message after a redirect (e.g. save → redirect). Exists only in JSF, not in CDI.

Flash scope – example

OrderBean.java – setting a message before the redirect
public String save() {
    // ... save the order ...
    FacesContext.getCurrentInstance()
        .getExternalContext()
        .getFlash()
        .put("message", "Order saved successfully!");
    return "orders?faces-redirect=true";
}
orders.xhtml – showing the message after the redirect
<h:outputText value="#{flash.message}" rendered="#{not empty flash.message}"
              style="color:green" />

Historical context: JSF 1.x only had XML configuration in faces-config.xml (<managed-bean>), JSF 2.x introduced its own annotations, and with CDI integration (the standard today) beans are managed via @Named + CDI scopes. Native JSF scope annotations have been deprecated since JSF 2.3.

7. Error Handling – Example

JSF applications handle errors at several levels: field validation, programmatic error messages, global exception handling, and Ajax errors.

1. Field/validation errors (standard case)

Already covered by converters/validators in phase 3 of the lifecycle (see sections 1 and 9) – output via <h:message> or <h:messages>.

2. Programmatic error message in the bean

public String save() {
    if (orderService.isDuplicate(order)) {
        FacesContext.getCurrentInstance().addMessage("orderForm:orderNumber",
            new FacesMessage(FacesMessage.SEVERITY_ERROR,
                "Order number already exists", null));
        return null; // show the page again, the message appears
    }
    return "confirmation?faces-redirect=true";
}

3. Global exception handling (unexpected errors)

Unhandled exceptions in the lifecycle can be caught centrally via a custom ExceptionHandler, e.g. to redirect to an error page instead of showing a stack trace:

CustomExceptionHandler.java
public class CustomExceptionHandler extends ExceptionHandlerWrapper {

    private final ExceptionHandler wrapped;
    public CustomExceptionHandler(ExceptionHandler wrapped) { this.wrapped = wrapped; }

    @Override
    public ExceptionHandler getWrapped() { return wrapped; }

    @Override
    public void handle() throws FacesException {
        Iterator<ExceptionQueuedEvent> events = getUnhandledExceptionQueuedEvents().iterator();
        while (events.hasNext()) {
            Throwable t = events.next().getContext().getException();
            try {
                FacesContext fc = FacesContext.getCurrentInstance();
                fc.getExternalContext().getRequestMap().put("errorMessage", t.getMessage());
                fc.getApplication().getNavigationHandler()
                  .handleNavigation(fc, null, "/error?faces-redirect=true");
                fc.renderResponse();
            } finally {
                events.remove(); // mark as handled
            }
        }
        getWrapped().handle();
    }
}
CustomExceptionHandlerFactory.java + registration
public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory {
    public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) { super(parent); }

    @Override
    public ExceptionHandler getExceptionHandler() {
        return new CustomExceptionHandler(getWrapped().getExceptionHandler());
    }
}
<!-- faces-config.xml -->
<factory>
    <exception-handler-factory>
        com.example.CustomExceptionHandlerFactory
    </exception-handler-factory>
</factory>

4. Error pages for unhandled exceptions/HTTP codes (web.xml)

<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/error.xhtml</location>
</error-page>
<error-page>
    <error-code>404</error-code>
    <location>/notfound.xhtml</location>
</error-page>

5. Error handling for Ajax requests

A server-side error during an Ajax postback triggers no visible feedback by default. A JavaScript callback can be registered via onerror:

<h:commandButton value="Save" action="#{orderBean.save}">
    <f:ajax execute="@form" render="@form" onerror="handleAjaxError" />
</h:commandButton>

<script>
function handleAjaxError(data) {
    if (data.status === "error") {
        alert("An error occurred: " + data.description);
    }
}
</script>

8. Facelets & Expression Language

What are Facelets?

Since JSF 2.0, Facelets has been the standard view technology (replacing the older JSP). Pages are written as plain XHTML and enriched with JSF tag libraries. Key characteristics: templating (master pages, reusable fragments), composite components, compilation of the XHTML into a component tree at runtime, and – since it's "just" XHTML – good preview/editor support, because the file remains syntactically valid HTML even without server-side rendering.

Expression Language (EL)

EL is used to bind component attributes to bean properties or methods. JSF/Facelets uses the curly-brace syntax #{...} for this (deferred evaluation – can be both read and written, e.g. for value bindings). The ${...} syntax (immediate evaluation, read-only) comes from JSP/JSTL and should be avoided in Facelets.

ExpressionMeaning
#{bean.property}Value expression: reads/writes a bean property (getter/setter).
#{bean.doSomething}Method expression: calls a parameterless method, e.g. as an action.
#{bean.doSomething(param)}Method expression with parameters (since EL 2.2).
#{not empty bean.list}Operators: and, or, not, empty, comparisons such as ==, gt, lt.
#{facesContext}, #{view}, #{request}, #{session}, #{application}, #{flash}, #{resource}, #{cc}Implicit objects that EL provides without further declaration (#{cc} only inside a composite component).

Tag libraries

Facelets pages include several standard tag libraries via XML namespace:

PrefixNamespacePurpose / example tags
h:jsf/htmlHTML render components: h:form, h:inputText, h:commandButton, h:dataTable, h:message.
f:jsf/coreNon-visual core tags: f:convertDateTime, f:validateLongRange, f:ajax, f:param, f:facet, f:viewParam.
ui:jsf/faceletsTemplating: ui:composition, ui:insert, ui:define, ui:decorate, ui:include, ui:param.
composite:jsf/compositeBuilding custom composite components: composite:interface, composite:implementation, composite:attribute.
c:jsp/jstl/coreJSTL tags such as c:forEach, c:if – act at page build time, not in the component tree; useful for conditionally not creating parts of the page at all.
p:, o:Third-partyComponent libraries such as PrimeFaces (p:) or OmniFaces (o:) add extra components/utilities.

Using Ajax

<f:ajax> makes virtually any JSF component Ajax-capable without having to write JavaScript. Important attributes:

AttributeMeaning
executeWhich components' (values) are sent to the server and processed (@this, @form, @all, @none, or specific IDs).
renderWhich parts of the component tree are re-rendered after the response.
eventThe triggering DOM/JSF event, e.g. click, blur, valueChange (the default depends on the component).
listenerAn optional bean method that is called when triggered (in addition to the normal action).
onevent / onerrorJavaScript callbacks for successful or failed Ajax responses, respectively.

Possible values for execute and render

execute controls which components are included in processing (phases 2–4 of the lifecycle) before the request; render controls which parts of the component tree are updated in the browser DOM after the response (phase 6). Both attributes accept the same syntax: reserved @ keywords, concrete client IDs, or a space-separated combination of both.

ValueMeaning
@thisOnly the component that f:ajax is attached to. Default for execute.
@noneNothing at all. For execute this means: no values are processed/updated. Default for render – so by default nothing is re-rendered unless specified explicitly (except implicitly triggered error messages).
@formThe enclosing <h:form> of the component – by far the most common value, since it processes/updates all fields of a form.
@allThe entire view (the whole page) – for render this is effectively a "full Ajax reload" of the body; for execute, all form values on the page are processed.
Concrete client ID(s)One or more space-separated IDs, e.g. render="dobMsg ageMsg". IDs can be given relatively (within the same NamingContainer) or absolutely with a leading colon, e.g. :mainForm:summaryPanel.
@namingcontainer(since JSF 2.3) The nearest enclosing NamingContainer (e.g. an h:panelGroup with its own id, or a composite component) – handy for rendering relative to "my container" without hard-wiring its ID.
@composite(since JSF 2.3) The enclosing composite component, if the current component is located inside one.
@parent(since JSF 2.3) The direct parent component in the component tree.
@child(n)(since JSF 2.3) The n-th child (0-based) of the current component.
@previous / @next(since JSF 2.3) The previous or next sibling component at the same level, respectively.
@id(id)(since JSF 2.3) Looks up a component with this id regardless of its position in the tree – useful when the relative structure is unknown/variable.

Examples: render="@form" updates the entire form (e.g. after submitting with possible validation errors in several fields); render="@this" updates only the triggering component itself (e.g. a counter label); render="dobMsg ageMsg summaryPanel" updates several individual areas in a targeted way; render="@none" (or omitting it) suppresses any DOM update when only a listener method should run in the background. If a given ID isn't found, most implementations silently ignore that part of the specification instead of throwing an error – so when debugging "why isn't my panel updating", it's always worth checking for typos in the ID or the correct NamingContainer prefix.

Example: dependent select field (country → city) via Ajax
<h:selectOneMenu value="#{orderBean.country}">
    <f:selectItems value="#{orderBean.countries}" />
    <f:ajax listener="#{orderBean.onCountryChange}" render="citySelect" />
</h:selectOneMenu>

<h:selectOneMenu id="citySelect" value="#{orderBean.city}">
    <f:selectItems value="#{orderBean.citiesForSelectedCountry}" />
</h:selectOneMenu>

9. Concrete Facelets Example

A template with ui:insert, a page that uses it via ui:composition and ui:define, and a form with a converter, validator, and Ajax.

/WEB-INF/templates/layout.xhtml (master template)
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
    <title><ui:insert name="title">My App</ui:insert></title>
</h:head>
<h:body>
    <div id="header"><ui:insert name="header">Default header</ui:insert></div>
    <div id="content"><ui:insert name="content" /></div>
    <div id="footer">© 2026 – My App</div>
</h:body>
</html>
/register.xhtml (uses the template)
<ui:composition template="/WEB-INF/templates/layout.xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

    <ui:define name="title">Registration</ui:define>

    <ui:define name="content">
        <h:form>
            <h:panelGrid columns="3">

                <h:outputLabel value="Date of birth" for="dob" />
                <h:inputText id="dob" value="#{registerBean.birthDate}">
                    <f:convertDateTime pattern="dd.MM.yyyy" />
                    <f:ajax event="blur" render="dobMsg" />
                </h:inputText>
                <h:message id="dobMsg" for="dob" style="color:red" />

                <h:outputLabel value="Age" for="age" />
                <h:inputText id="age" value="#{registerBean.age}">
                    <f:validateLongRange minimum="18" maximum="120" />
                    <f:ajax event="blur" render="ageMsg" />
                </h:inputText>
                <h:message id="ageMsg" for="age" style="color:red" />

            </h:panelGrid>

            <h:commandButton value="Submit" action="#{registerBean.submit}" />
        </h:form>
    </ui:define>
</ui:composition>
RegisterBean.java (backing bean)
@Named
@ViewScoped
public class RegisterBean implements Serializable {

    private LocalDate birthDate;
    private int age;

    public String submit() {
        // business logic, e.g. save to DB
        return "success?faces-redirect=true";
    }

    // getters & setters
    public LocalDate getBirthDate() { return birthDate; }
    public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}

Lifecycle flow: On submit, the strings from dob/age are applied in phase 2, converted/validated in phase 3 via f:convertDateTime and f:validateLongRange respectively (on error → straight to Render Response with h:message), written into the bean properties in phase 4, submit() is called in phase 5, and the redirect to the next page happens in phase 6.

10. Showing and Hiding UI Elements

There are three common approaches in JSF for showing/hiding sections – each with different trade-offs in server traffic, security, and responsiveness.

VariantServer round tripCharacteristic
A: rendered without AjaxYes (full postback)With rendered="false" the element is not written into the HTML at all. The entire page is reloaded.
B: purely client-side (CSS/JS)NoImmediate reaction without server contact, but the element stays in the DOM (only hidden) – not suitable for sensitive content.
C: rendered with AjaxYes (only a partial area)Like A, but only the affected panel area is re-rendered – no full page reload, better UX.

A: Without Ajax – server-side via rendered

The classic approach: a boolean field in the bean controls the rendered attribute. Every click triggers a full postback, and the entire page is rebuilt and re-rendered.

form.xhtml
<h:form>
    <h:selectBooleanCheckbox value="#{formBean.showDetails}" />
    <h:outputLabel value="Show additional info" />
    <h:commandButton value="Refresh" action="#{formBean.refresh}" />

    <h:panelGroup rendered="#{formBean.showDetails}" layout="block">
        <h:outputLabel value="Comment" for="comment" />
        <h:inputTextarea id="comment" value="#{formBean.details}" />
    </h:panelGroup>
</h:form>
FormBean.java
@Named
@ViewScoped
public class FormBean implements Serializable {
    private boolean showDetails;
    private String details;

    public String refresh() { return null; } // no navigation target, just a postback
    // getters & setters
}

B: Without Ajax – purely client-side (no server contact)

If no server data is involved, plain JavaScript/CSS is enough – faster, but the element stays (hidden) in the DOM and is still transferred to the client.

form.xhtml
<h:commandButton type="button" value="Show/hide details"
    onclick="var p = document.getElementById('detailsPanel');
             p.style.display = (p.style.display === 'none') ? 'block' : 'none';" />

<h:panelGroup id="detailsPanel" layout="block" style="display:none">
    <h:outputText value="This additional info is only shown/hidden in the browser." />
</h:panelGroup>

type="button" prevents the button from submitting a form. Since no rendered logic applies here, the content always remains present in the component tree and in the HTML.

C: With Ajax – server-side via rendered + f:ajax

Combines the best of both worlds: the state is still controlled server-side via the bean (so with rendered="false" the element really isn't in the HTML), but only the enclosing area is re-rendered – no page reload.

form.xhtml
<h:form>
    <h:selectBooleanCheckbox value="#{formBean.showDetails}">
        <f:ajax render="detailsPanel" />
    </h:selectBooleanCheckbox>
    <h:outputLabel value="Show additional info" />

    <h:panelGroup id="detailsPanel" layout="block">
        <h:panelGrid columns="2" rendered="#{formBean.showDetails}">
            <h:outputLabel value="Comment" for="comment" />
            <h:inputTextarea id="comment" value="#{formBean.details}" />
        </h:panelGrid>
    </h:panelGroup>
</h:form>

Flow: h:selectBooleanCheckbox triggers an Ajax request on change by default; showDetails is updated in phase 4 (Update Model Values), and phase 6 (Render Response) then returns only the detailsPanel, since that's exactly what was requested via render. An additional listener isn't needed here, since the value binding alone is enough to control visibility.

11. Bean Validation (JSR 380)

Instead of declaring validation via f:validate* tags in the view, rules can be annotated directly on the model (Jakarta Bean Validation, formerly JSR 303/380). By default, JSF automatically invokes a BeanValidator for this in phase 3 (Process Validations) – with no extra configuration needed.

UserDto.java – validation rules on the model
public class UserDto {

    @NotNull
    @Size(min = 2, max = 50)
    private String name;

    @NotNull
    @Email
    private String email;

    @Min(18)
    private int age;

    // getters & setters
}
RegisterBean.java
@Named
@ViewScoped
public class RegisterBean implements Serializable {

    private final UserDto user = new UserDto();

    public String submit() {
        // only reached if all bean validation constraints are satisfied
        return "success?faces-redirect=true";
    }

    public UserDto getUser() { return user; }
}
register.xhtml
<h:form>
    <h:inputText value="#{registerBean.user.name}" />
    <h:message for="..." />

    <h:inputText value="#{registerBean.user.email}" />
    <h:inputText value="#{registerBean.user.age}">
        <f:validateBean disabled="false" />
    </h:inputText>

    <h:commandButton value="Register" action="#{registerBean.submit}" />
</h:form>

With <f:validateBean validationGroups="..."/> you can selectively activate validation groups, and with disabled="true" you can turn off automatic bean validation for individual fields (e.g. when only a custom validator should run there).

12. Custom Converter & Validator – Code Example

Custom converter

A converter converts between the display string in the browser and a Java object – here's an example that resolves a product ID into a full Product object (useful e.g. in h:selectOneMenu).

ProductConverter.java
@FacesConverter(value = "productConverter", managed = true)
public class ProductConverter implements Converter<Product> {

    @Inject
    private ProductService productService;

    @Override
    public Product getAsObject(FacesContext context, UIComponent component, String value) {
        if (value == null || value.isBlank()) return null;
        return productService.findById(Long.valueOf(value));
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Product product) {
        return product == null ? "" : String.valueOf(product.getId());
    }
}
Usage
<h:selectOneMenu value="#{orderBean.selectedProduct}" converter="productConverter">
    <f:selectItems value="#{orderBean.availableProducts}" var="p"
                   itemLabel="#{p.name}" itemValue="#{p}" />
</h:selectOneMenu>

managed = true ensures that CDI injection (here @Inject ProductService) works inside the converter – without this attribute, JSF would instantiate the converter itself, without involving CDI.

Custom validator

StrongPasswordValidator.java
@FacesValidator(value = "strongPasswordValidator", managed = true)
public class StrongPasswordValidator implements Validator<String> {

    @Override
    public void validate(FacesContext context, UIComponent component, String value) {
        if (value == null || value.length() < 8 || !value.matches(".*[A-Z].*")) {
            throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR,
                "Password must be at least 8 characters long and contain an uppercase letter", null));
        }
    }
}
Usage
<h:inputSecret value="#{registerBean.password}" validator="strongPasswordValidator" />
<h:message for="password" />

13. File Upload

Since JSF 2.2, <h:inputFile> supports file uploads directly as a standard component – for this, the form needs enctype="multipart/form-data".

upload.xhtml
<h:form enctype="multipart/form-data">
    <h:inputFile value="#{uploadBean.file}" />
    <h:commandButton value="Upload" action="#{uploadBean.upload}" />
</h:form>
UploadBean.java
@Named
@RequestScoped
public class UploadBean {

    private Part file; // jakarta.servlet.http.Part

    public String upload() throws IOException {
        try (InputStream in = file.getInputStream()) {
            Files.copy(in, Paths.get("/uploads/" + file.getSubmittedFileName()),
                       StandardCopyOption.REPLACE_EXISTING);
        }
        return "success?faces-redirect=true";
    }

    public Part getFile() { return file; }
    public void setFile(Part file) { this.file = file; }
}
web.xml – limiting the maximum upload size
<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <multipart-config>
        <max-file-size>10485760</max-file-size>      <!-- 10 MB per file -->
        <max-request-size>20971520</max-request-size> <!-- 20 MB total -->
    </multipart-config>
</servlet>

14. View Parameters & Post-Redirect-Get

<f:viewParam> binds GET query parameters to bean properties – this makes pages bookmarkable and safely repeatable via browser reload, without form data needing to be resubmitted.

product.xhtml
<f:metadata>
    <f:viewParam name="id" value="#{productBean.productId}" />
    <f:viewAction action="#{productBean.loadProduct}" />
</f:metadata>

<h:body>
    <h1>#{productBean.product.name}</h1>
    <p>#{productBean.product.description}</p>
</h:body>
ProductBean.java
@Named
@ViewScoped
public class ProductBean implements Serializable {

    private Long productId;
    private Product product;

    public void loadProduct() {
        product = productService.findById(productId);
    }
    // getters & setters
}
Bookmarkable link to the detail page
<h:link value="Details" outcome="product">
    <f:param name="id" value="#{p.id}" />
</h:link>
<!-- generates e.g.: /product.xhtml?id=42 -->

Post-Redirect-Get (PRG): An action method that responds to a POST with "...?faces-redirect=true" (see section 6) prevents a browser reload from resubmitting the form – after the redirect, the browser's history shows only a GET request. With &includeViewParams=true, any existing f:viewParam values are additionally appended to the target URL.

15. Resource Handling

In JSF, static resources (CSS, JavaScript, images) are organized via the /resources folder and included with dedicated tags instead of plain HTML tags. Benefits: automatic versioning/cache-busting, correct content-type headers, and support for "resource libraries" (e.g. theme-dependent swapping of CSS files).

Folder structure
/resources
    /css
        styles.css
    /js
        app.js
    /images
        logo.png
layout.xhtml
<h:head>
    <h:outputStylesheet name="styles.css" library="css" />
    <h:outputScript name="app.js" library="js" target="head" />
</h:head>
<h:body>
    <h:graphicImage name="logo.png" library="images" alt="Logo" />
</h:body>

JSF serves these resources via its own URL (e.g. /javax.faces.resource/styles.css.xhtml?ln=css) and appends a version stamp if needed, so that browser caches are automatically invalidated when a new version is deployed.

16. Internationalization (i18n)

Multilingual texts are managed via Java ResourceBundles (.properties files) and included in the page via EL.

faces-config.xml
<application>
    <locale-config>
        <default-locale>de</default-locale>
        <supported-locale>en</supported-locale>
    </locale-config>
    <resource-bundle>
        <base-name>messages</base-name>
        <var>msg</var>
    </resource-bundle>
</application>
messages_de.properties / messages_en.properties
welcomeText=Willkommen bei unserer Anwendung
saveButton=Speichern

# messages_en.properties
welcomeText=Welcome to our application
saveButton=Save
Usage in the page
<h:outputText value="#{msg.welcomeText}" />
<h:commandButton value="#{msg.saveButton}" action="#{formBean.save}" />
Switching the language programmatically
FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale("en"));

Alternatively, a bundle can also be loaded directly in a page without an entry in faces-config.xml: <f:loadBundle basename="messages" var="msg" />.

17. State Saving & CSRF

JSF has to keep the component tree "somewhere" between two requests for a postback to work. Where that happens is controlled by the state-saving method.

web.xml
<context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>server</param-value> <!-- or: client -->
</context-param>
MethodBehavior
server (default)The component tree state resides in server memory (bound to the session); the client only receives a reference to it in the hidden javax.faces.ViewState field. Lower client traffic, but memory usage per session and, typically, session affinity is needed in a cluster.
clientThe entire (serialized, typically encrypted) view state is embedded directly in the javax.faces.ViewState field. The server stays stateless (simpler clustering), but every request transfers more data.

CSRF protection: The javax.faces.ViewState field is tied to the session or a random view ID and must match exactly on postback – this already hampers classic CSRF attacks out of the box. Modern JSF implementations (Mojarra/MyFaces) additionally randomize the view state ID to avoid predictability.

Stateless views (without any view state)
<f:view transient="true">
    ...
</f:view>

For pure display pages without forms/postbacks, transient="true" avoids creating a view state entirely – neither server- nor client-side state saving is needed.

18. Faces Flows & PhaseListener

Faces Flows

Faces Flows (since JSF 2.2) encapsulate multi-page processes (e.g. a checkout process) including their own scope – a declarative alternative/complement to @ConversationScoped. A flow is created by convention: a folder with the flow's name that contains a matching <name>-flow.xml.

/checkout/checkout-flow.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              version="2.2">
    <flow-definition id="checkout">
        <start-node>step1</start-node>
        <flow-return id="checkoutComplete">
            <from-outcome>/confirmation</from-outcome>
        </flow-return>
    </flow-definition>
</faces-config>
CheckoutBean.java
@Named
@FlowScoped("checkout")
public class CheckoutBean implements Serializable {
    private Cart cart;
    // lives only as long as the user is within the "checkout" flow
}
Entry point into the flow
<h:link outcome="checkout/step1" value="Checkout" />

PhaseListener

A PhaseListener hooks in globally before/after specific lifecycle phases – useful for logging, monitoring, or cross-cutting concerns that don't belong in individual beans.

LoggingPhaseListener.java
public class LoggingPhaseListener implements PhaseListener {

    @Override
    public void beforePhase(PhaseEvent event) {
        System.out.println("Before phase: " + event.getPhaseId());
    }

    @Override
    public void afterPhase(PhaseEvent event) {
        System.out.println("After phase: " + event.getPhaseId());
    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.ANY_PHASE; // or specifically e.g. PhaseId.RENDER_RESPONSE
    }
}
Registration in faces-config.xml
<lifecycle>
    <phase-listener>com.example.LoggingPhaseListener</phase-listener>
</lifecycle>
Note on brand and product names: The product, company and brand names mentioned in this document — including JavaServer Faces (JSF), Jakarta EE, Java, CDI, JSP, JSTL, PrimeFaces, OmniFaces, Mojarra, MyFaces — are trademarks or registered trademarks of their respective rights holders. They are used here solely for illustrative and explanatory purposes; no affiliation, endorsement, or partnership with the respective companies is implied.