A common question which keeps returning is "How to display HTML in <h:messages>?". One would logically think to add an escape="false" attribute to the component, like as you would do in a <h:outputText>. Unfortunately, this is not possible in the standard JSF implementation. The component and the renderer does officially not support this attribute. The <h:outputText> and <f:selectItem> are as far the only which supports the escape attribute. Your best bet is to homegrow a renderer which handles this.
First some background information: JSF by default uses ResponseWriter#writeText() to write the tag body, which escapes HTML by default. We'd like to let it use ResponseWriter#write() instead like as with <h:outputText escape="false" />.
So, we'd like to extend the MessageRenderer of the standard JSF implementation and override the encodeEnd() method accordingly. But since the MessageRenderer#encodeEnd() contains pretty a lot of code (~180 lines) which we prefer not to copypaste to just change one or two lines after all, it's a better idea to replace the ResponseWriter with a custom implementation with help of ResponseWriterWrapper wherein the writeText() method is been overriden to handle the escaping.
So, I ended up with this:
package com.example; import java.io.IOException; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.context.ResponseWriterWrapper; import javax.faces.render.FacesRenderer; import com.sun.faces.renderkit.html_basic.MessagesRenderer; @FacesRenderer(componentFamily="javax.faces.Messages", rendererType="javax.faces.Messages") public class EscapableMessagesRenderer extends MessagesRenderer { @Override public void encodeEnd(FacesContext context, UIComponent component) throws IOException { final ResponseWriter originalResponseWriter = context.getResponseWriter(); context.setResponseWriter(new ResponseWriterWrapper() { @Override public ResponseWriter getWrapped() { return originalResponseWriter; } @Override public void writeText(Object text, UIComponent component, String property) throws IOException { String string = String.valueOf(text); String escape = (String) component.getAttributes().get("escape"); if (escape != null && !Boolean.valueOf(escape)) { super.write(string); } else { super.writeText(string, component, property); } } }); super.encodeEnd(context, component); context.setResponseWriter(originalResponseWriter); // Restore original writer. } }
But, in spite of the @FacesRenderer annotation, it get overriden by the default MessagesRenderer implementation. Since I suspect a bug here, I reported issue 1748. To get it to work anyway, we have to fall back to the faces-config.xml:
<render-kit> <renderer> <component-family>javax.faces.Messages</component-family> <renderer-type>javax.faces.Messages</renderer-type> <renderer-class>com.example.EscapableMessagesRenderer</renderer-class> </renderer> </render-kit>
And it works! :) Use it as follows:
<h:messages escape="false" />
To do the same for <h:message>, just copy the above and replace anywhere "Messages" appears in the code (component family, renderer type and class names) by "Message".
The above is written with JSF 2.0 in mind, but it should also just work in JSF 1.2, you only have to remove the @FacesRenderer annotation. It will not work in JSF 1.1 or older since there's no ResponseWriter#writeText() method which takes an UIComponent as argument.
Update: a ready to use solution is available in OmniFaces as <o:messages>
.