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"

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

        <composite:insertChildren />
public class CustomPanelGroup extends UINamingContainer {

    private String tagName;

    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);

    public void encodeEnd(FacesContext context) throws IOException {
        ResponseWriter writer = context.getResponseWriter();