SelectCheckboxMenu is used to choose multiple items displayed in an overlay.
<div class="card">
    <h:form>
        <p:growl>
            <p:autoUpdate/>
        </p:growl>
        <h5 class="mt-0">Basic</h5>
        <p:selectCheckboxMenu id="menu"
                              dynamic="true"
                              filter="true" filterMatchMode="startsWith"
                              label="Cities" updateLabel="true" showSelectAll="false"
                              selectedLabel="Cities Selected"
                              style="width: 15rem" panelStyle="width: 15rem" scrollHeight="250"
                              value="#{checkboxView.selectedCities}">
            <f:selectItems value="#{checkboxView.cities}"/>
            <f:facet name="footer">
                <p:divider styleClass="mt-0" />
                <h:outputText value="#{checkboxView.cities.size()} cities"
                              styleClass="py-1 block font-bold"/>
            </f:facet>
        </p:selectCheckboxMenu>
        <h5>Multiple</h5>
        <p:selectCheckboxMenu id="multiple"
                              multiple="true" emptyLabel="Please select..." updateLabel="true"
                              style="min-width: 15rem" panelStyle="width: 30rem" scrollHeight="250"
                              value="#{checkboxView.selectedCities2}">
                <p:ajax event="toggleSelect" listener="#{checkboxView.onToggleSelect}"/>
                <p:ajax event="itemSelect" listener="#{checkboxView.onItemSelect}"/>
                <p:ajax event="itemUnselect" listener="#{checkboxView.onItemUnselect}"/>
                <f:selectItems value="#{checkboxView.cities}"/>
            </p:selectCheckboxMenu>
        <h5>Grouped</h5>
        <p:selectCheckboxMenu id="grouped"
                              filter="true" filterMatchMode="startsWith"
                              multiple="true" updateLabel="true"
                              style="width: 15rem" panelStyle="width: 15rem" scrollHeight="250"
                              value="#{checkboxView.selectedCountries}">
            <f:selectItems value="#{checkboxView.countries}"/>
        </p:selectCheckboxMenu>
        <h5>Advanced</h5>
        <p:selectCheckboxMenu id="advanced"
                              converter="#{countryConverter}"
                              filter="true" filterMatchMode="startsWith" filterNormalize="true"
                              multiple="true"  updateLabel="true"
                              scrollHeight="250"
                              value="#{checkboxView.selectedCountries2}"
                              var="c"
                              style="min-width: 15rem">
            <f:selectItems value="#{checkboxView.countries2}"/>
            <p:column style="width:10%">
                <span class="flag flag-#{c.code}" style="width: 30px; height: 20px"/>
            </p:column>
            <p:column>
                <f:facet name="header">
                    <h:outputText value="Name"/>
                </f:facet>
                <h:outputText value="#{c.name}"/>
            </p:column>
            <f:facet name="footer">
                <h:outputText value="#{checkboxView.countries2.size()} countries"
                              styleClass="py-1 block font-bold"/>
            </f:facet>
        </p:selectCheckboxMenu>
    </h:form>
</div>
package org.primefaces.showcase.view.input;
import org.primefaces.event.SelectEvent;
import org.primefaces.event.ToggleSelectEvent;
import org.primefaces.event.UnselectEvent;
import org.primefaces.showcase.domain.Country;
import org.primefaces.showcase.service.CountryService;
import java.util.ArrayList;
import java.util.List;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.model.SelectItem;
import jakarta.faces.model.SelectItemGroup;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Named
@RequestScoped
public class CheckboxView {
    private String[] selectedOptions;
    private String[] selectedOptions2;
    private String[] selectedCities;
    private String[] selectedCities2;
    private List<String> cities;
    private List<SelectItem> countries;
    private String[] selectedCountries;
    private List<Country> countries2;
    private List<Country> selectedCountries2;
    private List<SelectItem> countries3;
    private List<Country> selectedCountries3;
    @Inject
    private CountryService service;
    @PostConstruct
    public void init() {
        cities = new ArrayList<>();
        cities.add("Miami");
        cities.add("London");
        cities.add("Paris");
        cities.add("Istanbul");
        cities.add("Berlin");
        cities.add("Barcelona");
        cities.add("Rome");
        cities.add("Brasilia");
        cities.add("Amsterdam");
        countries = new ArrayList<>();
        SelectItemGroup europeCountries = new SelectItemGroup("European Countries");
        europeCountries.setSelectItems(new SelectItem[]{
            new SelectItem("Germany", "Germany"),
            new SelectItem("Turkey", "Turkey"),
            new SelectItem("Spain", "Spain")
        });
        SelectItemGroup americaCountries = new SelectItemGroup("American Countries");
        americaCountries.setSelectItems(new SelectItem[]{
            new SelectItem("United States", "United States"),
            new SelectItem("Brazil", "Brazil"),
            new SelectItem("Mexico", "Mexico")
        });
        countries.add(europeCountries);
        countries.add(americaCountries);
        countries2 = service.getCountries();
        countries3 = new ArrayList<>();
        SelectItemGroup europeCountries3 = new SelectItemGroup("European Countries");
        europeCountries3.setSelectItems(isoCodesToSelectItemArray("DE", "TR", "ES"));
        SelectItemGroup americaCountries3 = new SelectItemGroup("American Countries");
        americaCountries3.setSelectItems(isoCodesToSelectItemArray("US", "BR", "MX"));
        countries3.add(europeCountries3);
        countries3.add(americaCountries3);
    }
    public String[] getSelectedOptions() {
        return selectedOptions;
    }
    public void setSelectedOptions(String[] selectedOptions) {
        this.selectedOptions = selectedOptions;
    }
    public String[] getSelectedOptions2() {
        return selectedOptions2;
    }
    public void setSelectedOptions2(String[] selectedOptions2) {
        this.selectedOptions2 = selectedOptions2;
    }
    public String[] getSelectedCities() {
        return selectedCities;
    }
    public void setSelectedCities(String[] selectedCities) {
        this.selectedCities = selectedCities;
    }
    public String[] getSelectedCities2() {
        return selectedCities2;
    }
    public void setSelectedCities2(String[] selectedCities2) {
        this.selectedCities2 = selectedCities2;
    }
    public List<String> getCities() {
        return cities;
    }
    public void setCities(List<String> cities) {
        this.cities = cities;
    }
    public List<SelectItem> getCountries() {
        return countries;
    }
    public void setCountries(List<SelectItem> countries) {
        this.countries = countries;
    }
    public String[] getSelectedCountries() {
        return selectedCountries;
    }
    public void setSelectedCountries(String[] selectedCountries) {
        this.selectedCountries = selectedCountries;
    }
    public List<Country> getCountries2() {
        return countries2;
    }
    public void setCountries2(List<Country> countries2) {
        this.countries2 = countries2;
    }
    public List<Country> getSelectedCountries2() {
        return selectedCountries2;
    }
    public void setSelectedCountries2(List<Country> selectedCountries2) {
        this.selectedCountries2 = selectedCountries2;
    }
    public List<SelectItem> getCountries3() {
        return countries3;
    }
    public void setCountries3(List<SelectItem> countries3) {
        this.countries3 = countries3;
    }
    public List<Country> getSelectedCountries3() {
        return selectedCountries3;
    }
    public void setSelectedCountries3(List<Country> selectedCountries3) {
        this.selectedCountries3 = selectedCountries3;
    }
    public void onToggleSelect(ToggleSelectEvent event) {
        FacesMessage msg = new FacesMessage();
        msg.setSummary("Toggled: " + event.isSelected());
        msg.setSeverity(FacesMessage.SEVERITY_INFO);
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }
    public void onItemSelect(SelectEvent event) {
        FacesMessage msg = new FacesMessage();
        msg.setSummary("Item selected: " + event.getObject().toString());
        msg.setSeverity(FacesMessage.SEVERITY_INFO);
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }
    public void onItemUnselect(UnselectEvent event) {
        FacesMessage msg = new FacesMessage();
        msg.setSummary("Item unselected: " + event.getObject().toString());
        msg.setSeverity(FacesMessage.SEVERITY_INFO);
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }
    public void selectedOptionsChanged() {
        String message = "selectedOptions changed to: ";
        if (selectedOptions != null) {
            for (int i = 0; i < selectedOptions.length; i++) {
                if (i > 0) {
                    message += ", ";
                }
                message += selectedOptions[i];
            }
        }
        FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO, message, null));
    }
    public void selectedOptions2Changed() {
        String message = "selectedOptions2 changed to: ";
        if (selectedOptions2 != null) {
            for (int i = 0; i < selectedOptions2.length; i++) {
                if (i > 0) {
                    message += ", ";
                }
                message += selectedOptions2[i];
            }
        }
        FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO, message, null));
    }
    private SelectItem[] isoCodesToSelectItemArray(String... isoCodes) {
        return CountryService.toCountryStream(isoCodes)
                .map(country -> new SelectItem(country, country.getName()))
                .toArray(SelectItem[]::new);
    }
}
package org.primefaces.showcase.domain;
import java.io.Serializable;
import java.util.Locale;
import java.util.Objects;
public class Country implements Serializable, Comparable<Country> {
    private int id;
    private String name;
    private String code;
    private Locale locale;
    private boolean rtl;
    public Country() {
    }
    public Country(int id, Locale locale) {
        this(id, locale.getDisplayCountry(), locale.getCountry().toLowerCase(), locale);
    }
    public Country(int id, Locale locale, boolean rtl) {
        this(id, locale.getDisplayCountry(), locale.getCountry().toLowerCase(), locale);
        this.rtl = rtl;
    }
    public Country(int id, String name, String code) {
        this(id, name, code, null);
    }
    public Country(int id, String name, String code, Locale locale) {
        this.id = id;
        this.name = name;
        this.code = code;
        this.locale = locale;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public Locale getLocale() {
        return locale;
    }
    public void setLocale(Locale locale) {
        this.locale = locale;
    }
    public String getLanguage() {
        return locale == null ? "en" : locale.getLanguage();
    }
    public String getDisplayLanguage() {
        return locale == null ? "English" : locale.getDisplayLanguage();
    }
    public boolean isRtl() {
        return rtl;
    }
    public void setRtl(boolean rtl) {
        this.rtl = rtl;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Country country = (Country) o;
        return id == country.id
                && Objects.equals(name, country.name)
                && Objects.equals(code, country.code);
    }
    @Override
    public int hashCode() {
        return Objects.hash(id, name, code);
    }
    @Override
    public String toString() {
        return name;
    }
    @Override
    public int compareTo(Country o) {
        return name.compareTo(o.name);
    }
}
package org.primefaces.showcase.service;
import org.primefaces.showcase.domain.Country;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;
@Named
@ApplicationScoped
public class CountryService {
    private List<Country> countries;
    private Map<Integer, Country> countriesAsMap;
    private List<Country> locales;
    private Map<Integer, Country> localesAsMap;
    public static Stream<Country> toCountryStream(String... isoCodes) {
        return Stream.of(isoCodes)
                .map(isoCode -> new Locale("", isoCode))
                .map(CountryService::toCountry);
    }
    public static Country toCountry(Locale locale) {
        return CountryService.toCountry(locale, false);
    }
    public static Country toCountry(Locale locale, boolean rtl) {
        //use hash code from locale to have a reproducible ID (required for CountryConverter)
        return new Country(locale.hashCode(), locale, rtl);
    }
    @PostConstruct
    public void init() {
        countries = CountryService.toCountryStream(Locale.getISOCountries())
                .sorted(Comparator.comparing(Country::getName))
                .collect(Collectors.toList());
        locales = new ArrayList<>();
        locales.add(CountryService.toCountry(Locale.US));
        locales.add(CountryService.toCountry(Locale.UK));
        locales.add(CountryService.toCountry(new Locale("en", "AU")));
        locales.add(CountryService.toCountry(Locale.FRANCE));
        locales.add(CountryService.toCountry(Locale.GERMANY));
        locales.add(CountryService.toCountry(new Locale("de", "AT")));
        locales.add(CountryService.toCountry(new Locale("de", "CH")));
        locales.add(CountryService.toCountry(Locale.ITALY));
        locales.add(CountryService.toCountry(Locale.KOREA));
        locales.add(CountryService.toCountry(new Locale("es", "ES")));
        locales.add(CountryService.toCountry(new Locale("ca", "ES")));
        locales.add(CountryService.toCountry(new Locale("nl", "NL")));
        locales.add(CountryService.toCountry(new Locale("pt", "BR")));
        locales.add(CountryService.toCountry(new Locale("pt", "PT")));
        locales.add(CountryService.toCountry(new Locale("ar", "SA"), true));
        locales.add(CountryService.toCountry(new Locale("ar", "TN"), true));
        locales.add(CountryService.toCountry(new Locale("bg", "BG")));
        locales.add(CountryService.toCountry(new Locale("bn", "BD")));
        locales.add(CountryService.toCountry(new Locale("bs", "BA")));
        locales.add(CountryService.toCountry(new Locale("cs", "CZ")));
        locales.add(CountryService.toCountry(new Locale("el", "GR")));
        locales.add(CountryService.toCountry(new Locale("fa", "IR"), true));
        locales.add(CountryService.toCountry(new Locale("fi", "FI")));
        locales.add(CountryService.toCountry(new Locale("da", "DK")));
        locales.add(CountryService.toCountry(new Locale("hi", "IN")));
        locales.add(CountryService.toCountry(new Locale("in", "ID")));
        locales.add(CountryService.toCountry(new Locale("hr", "HR")));
        locales.add(CountryService.toCountry(new Locale("ja", "JP")));
        locales.add(CountryService.toCountry(new Locale("hu", "HU")));
        locales.add(CountryService.toCountry(new Locale("he", "IL"), true));
        locales.add(CountryService.toCountry(new Locale("ka", "GE")));
        locales.add(CountryService.toCountry(new Locale("ckb", "IQ"), true));
        locales.add(CountryService.toCountry(new Locale("km", "KH")));
        locales.add(CountryService.toCountry(new Locale("ky", "KG")));
        locales.add(CountryService.toCountry(new Locale("kk", "KZ")));
        locales.add(CountryService.toCountry(new Locale("lt", "LT")));
        locales.add(CountryService.toCountry(new Locale("lv", "LV")));
        locales.add(CountryService.toCountry(new Locale("ms", "MY")));
        locales.add(CountryService.toCountry(new Locale("no", "NO")));
        locales.add(CountryService.toCountry(new Locale("pl", "PL")));
        locales.add(CountryService.toCountry(new Locale("ro", "RO")));
        locales.add(CountryService.toCountry(new Locale("ru", "RU")));
        locales.add(CountryService.toCountry(new Locale("sk", "SK")));
        locales.add(CountryService.toCountry(new Locale("sl", "SI")));
        locales.add(CountryService.toCountry(new Locale("sr", "BA")));
        locales.add(CountryService.toCountry(new Locale("sr", "RS")));
        locales.add(CountryService.toCountry(new Locale("sv", "SE")));
        locales.add(CountryService.toCountry(new Locale("th", "TH")));
        locales.add(CountryService.toCountry(new Locale("tr", "TR")));
        locales.add(CountryService.toCountry(new Locale("uk", "UA")));
        locales.add(CountryService.toCountry(new Locale("uz", "UZ")));
        locales.add(CountryService.toCountry(new Locale("vi", "VN")));
        locales.add(CountryService.toCountry(Locale.SIMPLIFIED_CHINESE));
        locales.add(CountryService.toCountry(Locale.TRADITIONAL_CHINESE));
    }
    public List<Country> getCountries() {
        return new ArrayList<>(countries);
    }
    public Map<Integer, Country> getCountriesAsMap() {
        if (countriesAsMap == null) {
            countriesAsMap = getCountries().stream().collect(Collectors.toMap(Country::getId, country -> country));
        }
        return countriesAsMap;
    }
    public List<Country> getLocales() {
        return new ArrayList<>(locales);
    }
    public Map<Integer, Country> getLocalesAsMap() {
        if (localesAsMap == null) {
            localesAsMap = getLocales().stream().collect(Collectors.toMap(Country::getId, country -> country));
        }
        return localesAsMap;
    }
}
package org.primefaces.showcase.convert;
import org.primefaces.showcase.domain.Country;
import org.primefaces.showcase.service.CountryService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.convert.Converter;
import jakarta.faces.convert.ConverterException;
import jakarta.faces.convert.FacesConverter;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Named
@ApplicationScoped
@FacesConverter(value = "countryConverter", managed = true)
public class CountryConverter implements Converter<Country> {
    @Inject
    private CountryService countryService;
    @Override
    public Country getAsObject(FacesContext context, UIComponent component, String value) {
        if (value != null && !value.trim().isEmpty()) {
            try {
                return countryService.getCountriesAsMap().get(Integer.parseInt(value));
            }
            catch (NumberFormatException e) {
                throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid country."));
            }
        }
        else {
            return null;
        }
    }
    @Override
    public String getAsString(FacesContext context, UIComponent component, Country value) {
        if (value != null) {
            return String.valueOf(value.getId());
        }
        else {
            return null;
        }
    }
}