Currently, writing custom leaderboards become an issue when you realise there’s a lot of weird boilerplate that needs implementation if you want a close copy to how the actual playerlist works.
So, because of that, I made a wrapper service that does the messy baggage boilerplate for you.
Introducing
leaderstats2
https://github.com/metatablecat/leaderstats2
leaderstats2 provides a service script that creates a custom leaderstats, by default, it will create the classic 2015-2020 player list.

Creating a custom GUI
To create your own Gui, delete everything except the Util module under it, then in the empty Gui script, paste this in:
You may want to use some code from the
Utilmodule, but it also provides useful type information.
local Util = require(script.Util)
return function(API: Util.ProviderAPI)
end
At minimum, you should depend upon the API.PlayerAdded and API.PlayerRemoving events to detect when players have joined or left the game, the return is the wrapped list item.
API.PlayerAdded:Connect(function(player)
print(`{player.Name} is now in the list`)
end)
API.PlayerRemoving:Connect(function(player)
print(`{player.Name} is no longer in the list`)
end)
The returned function is called when the service loads, you should depend upon the various events to react to the state of the PlayerList service, such as players and teams being added/removed, as well as all known leaderstats being updated, for example, the implementation of the default PlayerList does the following when the AllLeaderstatShapeChanged event is fired
API.AllLeaderstatShapeChanged:Connect(function()
-- reshaping refers to adding or removing leaderstat squares in the list for each item
generateHeaderLabels(LEADERSTAT_ORDER) -- reshapes the Players label at the top
neutralStatLabels(LEADERSTAT_ORDER) -- reshapes the Neutral label
for _, plr in API:GetPlayers() do
recomputeLeaderstats(plr, LEADERSTAT_ORDER) -- reshapes each player's label
end
for _, team in API:GetTeams() do
recomputeLeaderstats(team, LEADERSTAT_ORDER) -- reshapes each team's label
end
sort(API) -- run the default sorting engine which reorders all tracked list items based on Util.sort
end)
Issues
Please use GitHub to report issues with this service