Roblox GUI Maker logoRoblox GUI Maker
Unofficial third-party tool · Not affiliated with Roblox Corporation

Roblox Menu Script Tutorial: Complete Main Menu System (2026)

2026-07-21 · Roblox GUI Maker Team

Menu structure: one shell, many pages

A good menu uses one shell Frame with a tab bar and a content area, and swaps the content panels when the player clicks a tab. Do not build separate full-screen menus for each page - that means duplicated transitions and more bugs. Instead, create one ScreenGui, put a tab bar on the left or top, and place each page (Play, Settings, Store) as a Frame inside the content area. Show one page and hide the rest; clicking a tab swaps which is visible with a Tween transition.

Page switching with Tween transitions

When the player clicks a tab, hide the current page and show the new one with a smooth transition. A common pattern is sliding the old page out and the new page in over 0.3 seconds. Use TweenService on each page's Position, and use a table to track which page is currently visible so the switch is clean. Keep transitions under 0.3 seconds - menus that take too long to open feel slow, especially on mobile.

StarterGui/MenuClient (LocalScript)
lua
1-- Main Menu - Client Script
2-- Place in: StarterGui (LocalScript)
3
4local Players = game:GetService("Players")
5local TweenService = game:GetService("TweenService")
6
7local player = Players.LocalPlayer
8local gui = script.Parent
9local pages = {
10 Play = gui:WaitForChild("PlayPage"),
11 Settings = gui:WaitForChild("SettingsPage"),
12 Store = gui:WaitForChild("StorePage"),
13}
14local currentPage = "Play"
15
16local function switchPage(name)
17 if name == currentPage then return end
18 local old = pages[currentPage]
19 local new = pages[name]
20 -- Slide old page out, new page in
21 TweenService:Create(old, TweenInfo.new(0.25, Enum.EasingStyle.Quad, Enum.EasingDirection.In),
22 { Position = UDim2.new(-1, 0, 0, 0) }):Play()
23 task.delay(0.25, function()
24 old.Visible = false
25 new.Position = UDim2.new(1, 0, 0, 0)
26 new.Visible = true
27 TweenService:Create(new, TweenInfo.new(0.25, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
28 { Position = UDim2.new(0, 0, 0, 0) }):Play()
29 end)
30 currentPage = name
31end
32
33-- Wire tab buttons
34for name, _ in pairs(pages) do
35 local tab = gui:WaitForChild("Tab_" .. name)
36 tab.MouseButton1Click:Connect(function()
37 switchPage(name)
38 end)
39end

Settings panel: sliders and toggles

The settings page gives players control over volume, graphics, and music. Use sliders (a draggable handle on a track) for volume and toggles (on/off switches) for music and fullscreen. Bind the volume slider to SoundService so changes apply immediately. Store the settings in a table and save to DataStore on change or on leave, so the player's preferences persist across sessions. The settings rows use UIListLayout so adding a new setting is one Frame.

Play button: starting the game

The Play button is the menu's only job. When clicked, fire a RemoteEvent to the server to start the match, and play a confirmation animation so the player knows it registered. Hide the menu after the match starts, or fade it out if the game begins immediately. On game over, show the menu again with the updated state (best time, coins, level). Wire the Play button with a short press animation so it feels responsive.

Mobile menu considerations

Most Roblox players are on phones, so the menu must work at 375px width. Use Scale-based sizing for every element, make buttons at least 60x60 logical pixels for imprecise taps, and use big, readable fonts (28+ for titles, 18+ for buttons). Test the tab bar on mobile - if tabs are too small to tap reliably, increase their size or move them to the bottom where thumbs reach easily.

Frequently asked questions