JSF tag h:panelGroup can be only:

  • a span (setting its attribute layout="span") or
  • a div (setting its attribute layout="block")

There are situations in which you would being able to choose another kind of HTML tag (i.e. to avoid inserting a span/div inside an ul/ol element or to comply with some CSS constraint that result from the usage of CSS/HTML frameworks as Bootstrap).

The following composite component simulates h:panelGroup behavior adding the possibility to set a custom tag.

<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:composite="http://java.sun.com/jsf/composite">

    <composite:interface componentType="customPanelGroup">
        <composite:attribute name="tag" required="true" />
    </composite:interface>

    <composite:implementation>
        <composite:insertChildren />
    </composite:implementation>
</html>
@FacesComponent("customPanelGroup")
public class CustomPanelGroup extends UINamingContainer {

    private String tagName;

    @Override
    public void encodeBegin(FacesContext context) throws IOException {

        tagName = (String) getAttributes().get("tag");

        // css "class" attribute
        String styleClass = (String) getAttributes().get("styleClass");

        ResponseWriter writer = context.getResponseWriter();
        writer.startElement(tagName, null);
        writer.writeAttribute("id", getClientId(), null);
        if (styleClass != null) {
            writer.writeAttribute("class", styleClass, null);
        }

        writePassThroughAttributes(context, writer);
    }

    private void writePassThroughAttributes(FacesContext context, ResponseWriter writer) throws IOException {
        for (Map.Entry<String, Object> entry : getPassThroughAttributes().entrySet()) {
            Object attrValue = entry.getValue();
            if (attrValue instanceof ValueExpression) {
                attrValue = ((ValueExpression) attrValue).getValue(context.getELContext());
            }
            writer.writeAttribute(entry.getKey(), attrValue, null);
        }
    }

    @Override
    public void encodeEnd(FacesContext context) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        writer.endElement(tagName);
    }
}