Roblox 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

Use this in your game

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.

Related guides

Skip the boilerplate

Everything in this guide — the hierarchy, the layout helpers, the button wiring — is built into the free Roblox GUI maker. Drag, drop, tweak real Roblox properties, then export clean Luau you can paste straight into Studio.