Roblox Shop GUI Tutorial: Build a Working Shop with Luau Code (2026)
2026-07-21 · Roblox GUI Maker Team
Why building a shop GUI by hand is slow
A working shop needs five moving parts: an item grid that reflows on mobile, category tabs that filter the grid, a Buy button per item that calls MarketplaceService, a server that validates receipts so players cannot fake purchases, and a Tween animation so the buy feels rewarding. Most tutorials cover one or two of these and leave you stuck on the rest. This one covers all five, with complete code.
Step 1: Design the shop layout with UIGridLayout
In this step you will lay out the item grid and category tabs. Start with a Frame anchored to the center of the screen, sized with Scale so it works on any device. Inside it, place a ScrollingFrame for the item grid and a horizontal Frame for the category tabs. The item grid uses UIGridLayout with a fixed cell size, so items arrange automatically and scroll when there are more than fit on screen. Use Scale-based sizing everywhere - a shop that only fits your monitor will break on a player's phone.
1-- Shop layout structure (build this in the web editor or in Studio)2-- ShopPanel (Frame, Scale 0.5 x 0.6, centered)3-- CategoryTabs (Frame, top bar)4-- Tab_Gear (TextButton)5-- Tab_PowerUps (TextButton)6-- Tab_Skins (TextButton)7-- ItemGrid (ScrollingFrame, UIListLayout or UIGridLayout)8-- GridLayout (UIGridLayout, CellSize 120x140)9-- CoinHeader (TextLabel, top-right, shows balance)
Step 2: Generate the client Luau
The client script builds the shop UI programmatically and wires each Buy button to call MarketplaceService. Below is the complete client code. Paste it into a LocalScript under StarterGui. Each item card specifies whether it is a Gamepass or Developer Product, and the Buy button calls the correct purchase method.
1-- Roblox Shop GUI - Client Script2-- Place in: StarterGui/ShopGui (LocalScript)34local Players = game:GetService("Players")5local MarketplaceService = game:GetService("MarketplaceService")6local TweenService = game:GetService("TweenService")7local ReplicatedStorage = game:GetService("ReplicatedStorage")89local player = Players.LocalPlayer10local gui = script.Parent11local itemGrid = gui:WaitForChild("ItemGrid")12local coinHeader = gui:WaitForChild("CoinHeader")1314local purchaseEvent = ReplicatedStorage:WaitForChild("ShopPurchaseResult")1516-- Item catalog: replace ids with your own Gamepass/Product ids17local ITEMS = {18 { id = 1001, name = "Sword", kind = "gamepass", price = 250 },19 { id = 1002, name = "Speed Boost", kind = "product", price = 100 },20 { id = 1003, name = "Golden Skin", kind = "product", price = 150 },21}2223local function playBuyFeedback(card)24 local flash = TweenService:Create(25 card,26 TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),27 { BackgroundColor3 = Color3.fromRGB(80, 200, 120) }28 )29 flash:Play()30 task.delay(0.4, function()31 card.BackgroundColor3 = Color3.fromRGB(30, 30, 40)32 end)33end3435for _, item in ipairs(ITEMS) do36 local card = itemGrid:WaitForChild(item.name)37 local buyButton = card:WaitForChild("BuyButton")38 buyButton.MouseButton1Click:Connect(function()39 if item.kind == "gamepass" then40 MarketplaceService:PromptGamepassPurchase(player, item.id)41 else42 MarketplaceService:PromptProductPurchase(player, item.id)43 end44 end)45end4647purchaseEvent.OnClientEvent:Connect(function(itemId, success)48 if success then49 local item = ITEMS[1]50 for _, it in ipairs(ITEMS) do51 if it.id == itemId then item = it end52 end53 if item then54 playBuyFeedback(itemGrid:WaitForChild(item.name))55 end56 end57end)
Step 3: Validate purchases on the server
Never trust the client for purchases. The server must validate every receipt with MarketplaceService:ProcessReceipt before granting the item. This is the step that prevents players from faking a buy with an exploit. The server also pushes the result back to the client so the UI can show the success animation.
1-- Roblox Shop GUI - Server Script2-- Place in: ServerScriptService (Script)34local MarketplaceService = game:GetService("MarketplaceService")5local ReplicatedStorage = game:GetService("ReplicatedStorage")6local purchaseEvent = ReplicatedStorage:WaitForChild("ShopPurchaseResult")78-- Grant the purchased item: replace with your real grant logic9local function grantItem(player, itemId)10 -- Example: add to inventory, give a tool, or award currency11 print(string.format("[Shop] Granting item %d to %s", itemId, player.Name))12end1314MarketplaceService.ProcessReceipt = function(receiptInfo)15 local player = game.Players:GetPlayerByUserId(receiptInfo.PlayerId)16 if not player then17 return Enum.ProductPurchaseDecision.NotProcessedYet18 end1920 -- Validate: confirm the player can receive this item21 grantItem(player, receiptInfo.ProductId)22 purchaseEvent:FireClient(player, receiptInfo.ProductId, true)2324 return Enum.ProductPurchaseDecision.PurchaseGranted25end
Step 4: Add Tween feedback on purchase
A shop that just silently grants an item feels dead. The playBuyFeedback function above flashes the purchased card green for 0.2 seconds when the server confirms the purchase. You can expand this with a scale-up Tween on the card, a coin counter that counts up to the new balance, or a sound effect. Keep the animation under half a second so it does not block the next purchase.
Common mistakes to avoid
- Granting items on the client - always validate with ProcessReceipt on the server
- Using Offset-only sizing - the shop breaks on mobile; use Scale everywhere
- Forgetting to check kind before prompting - Gamepass and Product use different methods
- No purchase feedback - players think nothing happened and buy again, costing Robux