You could use a module script of all the tools, see which ones they own in the datastore, then create a new frame on a ui with that tool.
Start off with obviously making the ui… then add a UIGridLayout or other, make a FrameTemplate to copy into the grid. To avoid confusion, thats just a regular frame but decorated how you want it to be.
The script will ask the module script what will go into the template when its created and then the second script (assuming it’ll be the UI script) will handle the item grabbing.
How the ui could work. Template frame/ button/ etc can be in the script for easiest access.

Your module script could look something like this,
local ToolsTable = {
["AK47"] = { -- This name would be the actual tools name, case sensitive.
OnlyOne = true, -- To check if they've already got one
Image = "rbxassetid://14557267785" -- For the UI, if you opt for ImageButtons
},
["OtherTool"] = { -- This name would be the actual tools name, case sensitive.
OnlyOne = true, -- To check if they've already got one
Image = "rbxassetid://14557267785" -- For the UI, if you opt for ImageButtons
},
}
return ToolsTable
This would be your server script, mind you this is was for a shop ui and does not include a datastore
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local StoreRegistry = require(ReplicatedStorage:WaitForChild("StoreRegistry")) -- the module script
local Func = ReplicatedStorage.StoreRegistry.RemoteFunction -- the remotefunction for client > server
local tools = game:GetService("ServerStorage"):WaitForChild("Tools", 5) -- THE TOOLS FOLDER!
Func.OnServerInvoke = function(plr, Item) -- plr, and then the item that will be called from the client
if StoreRegistry[buying] then
local char = plr.Character or plr.CharacterAdded:Wait()
local bcpk = plr.Backpack
if StoreRegistry[buying].OnlyOne == true then
if char:FindFirstChild(buying) or bcpk:FindFirstChild(buying) then
return "i can't carry more of these.."
end
end
local item = tools:FindFirstChild(buying) -- Checks the tools folder for an item with the same name as what was given. That's why the module script names must be exact
if not item then
return "this item doesnt exist!" -- in case you have an item in the module script that *doesnt exist in the tools folder*
end
item:Clone().Parent = bcpk -- gives the tool
return "purchased!"
else
return buying .. " is not a purchaseable product?"
end
end
And then the client script, which will send the information to the server script on what tool to give
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Options = Frame:WaitForChild("Options", 10) -- Your Frame in which the tool buttons will go
local Template = script:WaitForChild("Template", 10) -- The template!
local Registry = require(ReplicatedStorage:WaitForChild("StoreRegistry")) -- module script!!!!!!
for i, v in pairs(Registry) do -- This will look through all of the items in the Module Script and then add them as a button
-- You MUST add an "if" statement checking your datastore here if 'v' is in the datastore! Otherwise It'll just add all of the items from the module script.
local btn = Template:Clone() --Creates the button :)
btn.Name = i; btn.Image = v.Image -- This is where the button gets its Image, if you do the Image from the module script. I don't know how to use viewport frames so I cant help with that :(
btn.MouseButton1Click:Connect(function()
local response = ReplicatedStorage.StoreRegistry.RemoteFunction:InvokeServer(i) -- this is where it tells the server script!
end)
btn.Parent = Options -- sets the button to the proper frame so its visible.
end
Hope this helps! And PLEASE read all of the comments. You will have to edit these scripts a bunch to incorporate a datastore