This example shows how to implement lazy loading of child nodes - which is essential for big models.
<div class="card">
    <h:form id="form">
        <p:growl id="messages" showDetail="true"/>
        <p:tree value="#{treeLazyLoadingView.root}" var="node" dynamic="true">
            <p:treeNode>
                <h:outputText value="#{node.name}"/>
            </p:treeNode>
        </p:tree>
    </h:form>
</div>
package org.primefaces.showcase.view.data.tree;
import org.primefaces.model.LazyDefaultTreeNode;
import org.primefaces.model.TreeNode;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import jakarta.annotation.PostConstruct;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
@Named("treeLazyLoadingView")
@ViewScoped
public class LazyLoadingView implements Serializable {
    private TreeNode<FileInfo> root;
    @PostConstruct
    public void init() {
        FacesContext context = FacesContext.getCurrentInstance();
        root = new LazyDefaultTreeNode<>(new FileInfo(context.getExternalContext().getRealPath("/"), true),
                (fileInfo) -> listFiles(fileInfo),
                (fileInfo) -> !fileInfo.isDirectory());
    }
    public static List<FileInfo> listFiles(FileInfo parentFolder) {
        List<FileInfo> result = new ArrayList<>();
        File[] files = new File(parentFolder.getPath()).listFiles();
        if (files != null) {
            for (File file : files) {
                result.add(new FileInfo(file.getAbsolutePath(), file.isDirectory()));
            }
        }
        return result;
    }
    public TreeNode getRoot() {
        return root;
    }
}
package org.primefaces.showcase.view.data.tree;
import java.io.File;
import java.io.Serializable;
import java.util.Objects;
public class FileInfo implements Serializable {
    private final String path;
    private final String name;
    private final boolean directory;
    public FileInfo(String path, boolean directory) {
        this.directory = directory;
        if (path == null) {
            this.path = File.separator;
            this.name = File.separator;
        }
        else if (path.equals(File.separator)) {
            this.name = path;
            this.path = path;
        }
        else {
            String[] parts = path.split(File.separator.equals("\\") ? "\\\\" : File.separator);
            this.name = parts[parts.length - 1];
            this.path = path;
        }
    }
    public String getPath() {
        return path;
    }
    public String getName() {
        return name;
    }
    public boolean isDirectory() {
        return directory;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        FileInfo fileInfo = (FileInfo) o;
        return isDirectory() == fileInfo.isDirectory() && Objects.equals(getPath(), fileInfo.getPath()) && Objects.equals(getName(), fileInfo.getName());
    }
    @Override
    public int hashCode() {
        return Objects.hash(getPath(), getName(), isDirectory());
    }
    @Override
    public String toString() {
        return path + " / " + name;
    }
}