RRoblox GUI Maker
← All guides

Data

How to Make a Roblox Global Leaderboard GUI

A global leaderboard ranks every player by a stat like total coins, and the ranking persists across servers. The hard part isn't the GUI — it's storing sorted data with OrderedDataStore and reading the top players back. This guide covers the full loop: save a score, fetch the top 10, and render them as rows. Start from the leaderboard template for the row layout.

Open the Leaderboard template →

1. Store ranked data with OrderedDataStore

An OrderedDataStore keeps values sortable. Use the player's UserId as the key and their score as the value, so each player appears once.

local DataStoreService = game:GetService("DataStoreService")
local scores = DataStoreService:GetOrderedDataStore("CoinsLeaderboard")

local function saveScore(player, coins)
	scores:UpdateAsync(tostring(player.UserId), function(old)
		return math.max(old or 0, coins)
	end)
end

2. Read the top players

GetSortedAsync returns the entries in order. Pass false for descending (highest first) and a page size. GetCurrentPage gives the ranked list of { key, value }.

local function getTop(n)
	local page = scores:GetSortedAsync(false, n)
	local entries = {}
	for _, entry in ipairs(page:GetCurrentPage()) do
		local userId = tonumber(entry.key)
		local player = game:GetService("Players"):GetNameFromUserIdAsync(userId)
		table.insert(entries, { name = player, score = entry.value })
	end
	return entries
end
Tip: GetNameFromUserIdAsync is a web call — cache names so you don't re-fetch the same player every refresh.

3. Send the list to clients and build rows

Fire a RemoteEvent with the top list; each client builds a row per entry. Because the panel has a UIListLayout, parenting each row stacks it automatically.

-- Server
local remote = game.ReplicatedStorage:WaitForChild("LeaderboardUpdate")
task.spawn(function()
	while true do
		remote:FireAllClients(getTop(10))
		task.wait(60) -- refresh once a minute
	end
end)

-- Client: make one row per entry, parented to the panel
local function makeRow(rank, name, score)
	local row = Instance.new("Frame")
	row.Size = UDim2.fromScale(1, 0.14)
	row.Parent = panel
	-- ...rank label, name label, score label...
	return row
end

FAQ

How often should I refresh a global leaderboard?

OrderedDataStore has tight rate limits. Cache the top list and refresh every 30–60 seconds via a server loop, never on every score change.

Why does a player appear twice?

You're using something other than UserId as the key. Always key by tostring(player.UserId) so each player maps to exactly one entry.