-
The <o:notification> is a UIComponent which integrates the browser Web Notifications API with SSE (Server-Sent Events) based push. It opens a one-way (server to client) SSE connection and shows incoming push messages as browser notifications.
Prerequisites
This component requires the PWAResourceHandler to be activated by adding the following line to the <h:head> because it provides the service worker mandatory for cross-platform notification support via ServiceWorkerRegistration.showNotification():
<link rel="manifest" href="#{resource['omnifaces:manifest.webmanifest']}" />
The browser notification permission must be requested via a user gesture before any notification can be shown:
<button type="button" onclick="OmniFaces.Notification.requestPermission()">Enable Notifications</button>
The CDI backing bean must have at least one @Push(type=NOTIFICATION) qualified injection point using the channel name matching the channel attribute of this component, so that the SseEndpoint will be auto-activated.
Basic Usage
Declare the <o:notification> tag in the Faces view with a channel name.
<o:notification channel="notifications" />
In the server side, inject the push context and send notification messages created via createNotificationMessage(String, String).
@Inject @Push(type=NOTIFICATION)
private PushContext notifications;
public void sendNotification() {
notifications.send(Notification.createNotificationMessage("Order shipped", "Your order #1234 has been shipped."));
}
You can also add a URL so that clicking the notification will navigate to it:
notifications.send(Notification.createNotificationMessage("Order shipped", "Your order has been shipped.", "https://example.com/orders/1234"));
Relative URLs are also accepted:
notifications.send(Notification.createNotificationMessage("Order shipped", "Your order has been shipped.", "/orders/1234"));
User-targeted Notifications
The optional user attribute can be set to the unique identifier of the logged-in user, so that notifications can be targeted to a specific user.
<o:notification channel="notifications" user="#{request.remoteUser}" />
The notification can then be sent to a specific user:
notifications.send(Notification.createNotificationMessage("New message", "You have a new message."), recipientUserId);
Display behavior
By default, each new notification replaces the previous one from the same component. To let notifications stack independently instead, set the stacked attribute to true:
<o:notification channel="notifications" stacked="true" />
The requireInteraction attribute controls whether the notification remains visible until the user explicitly interacts with it (click or dismiss), rather than auto-closing after a timeout. This is useful for important alerts that should not be missed:
<o:notification channel="alerts" requireInteraction="true" />
The silent attribute suppresses the device's notification sound and vibration:
<o:notification channel="updates" silent="true" />
Event Handlers
The <o:notification> supports click and close event handlers.
<o:notification channel="notifications" onclick="handleClick" onclose="handleClose" />
The event's detail object contains data and tag. The data object contains the channel name and the optional url. The tag is set to the component's client ID (when not stacked) and is used by the browser for notification deduplication: a new notification with the same tag replaces the previous one instead of stacking.
function handleClick(event) {
console.log("Channel: " + event.detail.data.channel);
console.log("URL (if any): " + event.detail.data.url);
console.log("Tag (if not stacked): " + event.detail.tag);
}
function handleClose(event) {
console.log("Channel: " + event.detail.data.channel);
console.log("Tag (if not stacked): " + event.detail.tag);
}
When you need to invoke a server-side action on notification click, use <h:commandScript> to bridge the client-side event to the server. For example, to mark a notification as read when the user clicks it:
<o:notification channel="notifications" onclick="handleNotificationClick" />
<h:form>
<h:commandScript name="markAsRead" action="#{notificationBean.markAsRead}" />
</h:form>
With this handler:
function handleNotificationClick(event) {
markAsRead({ "notification.url": event.detail.data?.url });
}
And this bean:
public void markAsRead() {
var url = Faces.getRequestParameter("notification.url");
// ...
}
JavaScript API
The current permission can be checked via OmniFaces.Notification.getPermission(), which returns "granted", "denied", "default", or "unsupported".
const notificationPermission = OmniFaces.Notification.getPermission();
You can use OmniFaces.Notification.show to programmatically show notifications using JavaScript. The first argument is the channel name, the second the title, the optional third the body, and the optional fourth a URL:
OmniFaces.Notification.show("notifications", "Please wait ...");
OmniFaces.Notification.show("notifications", "Hello!", "This is a notification.");
OmniFaces.Notification.show("notifications", "Click me!", "Opens a link.", "https://omnifaces.org");
Platform limitations
The icon attribute is passed to the Notifications API, but custom notification icons are not guaranteed to work across all platforms. As of March 2026, macOS and Linux with GNOME always display the browser's own application icon instead, due to OS/desktop-environment-level policies. Custom icons do work on Windows and Android. Use PNG format at 192x192 or larger for best results.
<o:notification channel="alerts" icon="#{resource['icons/alert.png']}" />
Prerequisites
Browser notifications require explicit user permission. Press the button below to request permission. The current permission status is shown next to it.
Permission:
Basic notification
Press the button below to push a basic browser notification from the server. The notification will appear as a native OS notification.
Notification with URL
Press the button below to push a notification that navigates to the showcase homepage when clicked.
<h3>Prerequisites</h3>
<p>
Browser notifications require explicit user permission. Press the button below to request permission.
The current permission status is shown next to it.
</p>
<p>
<h:button value="Request notification permission" onclick="OmniFaces.Notification.requestPermission();return false;" />
Permission: <strong><span id="permissionStatus"></span></strong>
</p>
<!-- NOTE: having inline script in XHTML like below is bad practice. -->
<!-- It's included directly in XHTML just for sake of demo. -->
<!-- In real world code, put it in a JS file :) -->
<script>
function updatePermissionStatus(permission) {
$("#permissionStatus").text(permission);
}
function showUnsupported() {
$("#permissionStatus").text("unsupported");
}
document.addEventListener("DOMContentLoaded", function() {
$("#permissionStatus").text(OmniFaces.Notification.getPermission());
});
</script>
<!-- End of bad practice ;) -->
<h3>Basic notification</h3>
<p>
Press the button below to push a basic browser notification from the server.
The notification will appear as a native OS notification.
</p>
<h:form>
<h:commandButton value="Send notification!" action="#{notificationBean.sendBasic}">
<f:ajax />
</h:commandButton>
</h:form>
<h3>Notification with URL</h3>
<p>
Press the button below to push a notification that navigates to the showcase homepage when clicked.
</p>
<h:form>
<h:commandButton value="Send notification with link!" action="#{notificationBean.sendWithUrl}">
<f:ajax />
</h:commandButton>
</h:form>
<o:notification channel="notificationDemo" onpermissionchange="updatePermissionStatus" onunsupported="showUnsupported" />package org.omnifaces.showcase.push;
import static org.omnifaces.cdi.Push.Type.NOTIFICATION;
import static org.omnifaces.cdi.push.Notification.createNotificationMessage;
import java.io.Serializable;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.omnifaces.cdi.Push;
import org.omnifaces.cdi.PushContext;
import org.omnifaces.cdi.ViewScoped;
/**
* Backing bean for the {@code <o:notification>} showcase demo.
* Demonstrates sending browser notifications via SSE-based push.
*/
@Named
@ViewScoped
public class NotificationBean implements Serializable {
private static final long serialVersionUID = 1L;
@Inject @Push(type = NOTIFICATION, channel = "notificationDemo")
private PushContext notificationDemo;
/**
* Sends a basic browser notification with title and body.
*/
public void sendBasic() {
notificationDemo.send(createNotificationMessage("Hello from OmniFaces!", "This notification was pushed from the server via SSE."));
}
/**
* Sends a browser notification with a URL. Clicking it navigates to the showcase homepage.
*/
public void sendWithUrl() {
notificationDemo.send(createNotificationMessage("Visit OmniFaces", "Click to open the OmniFaces Showcase homepage.", "/"));
}
}
VDL documentation
API documentation
Java source code
org.omnifaces.cdi.push.SseEndpointorg.omnifaces.cdi.Pushorg.omnifaces.cdi.PushContextorg.omnifaces.cdi.push.Notificationorg.omnifaces.resourcehandler.PWAResourceHandlerorg.omnifaces.cdi.push.Sse