Use when adding buttons to the Roblox topbar (shop, inventory, settings, codes, music toggle) with TopbarPlus v3 (1foreverhd/topbarplus), or when editing code that calls Icon.new(). Covers install, the chainable Icon API, toggling windows, dropdowns, menus, notices, themes and respawn behaviour.
Use the robloxvibecoders MCP: call get_resource with id "skill-roblox-topbarplus" and install it as a Claude Code skill in this project (.claude/skills/roblox-topbarplus/SKILL.md).
Skills in ~/.claude/skills work in every project. Put them in your project's .claude/skills folder to share them with your team.
What your AI reads
SKILL.md
---
name: roblox-topbarplus
description: Use when adding buttons to the Roblox topbar (shop, inventory, settings, codes, music toggle) with TopbarPlus v3 (1foreverhd/topbarplus), or when editing code that calls Icon.new(). Covers install, the chainable Icon API, toggling windows, dropdowns, menus, notices, themes and respawn behaviour.
---
# Topbar icons with TopbarPlus v3
[TopbarPlus](https://github.com/1ForeverHD/TopbarPlus) by ForeverHD puts your own icons in the Roblox topbar, next to
the Roblox menu button. It handles PC, mobile, tablet and gamepad, moves extra icons into an overflow when space runs
out, and supports labels, images, captions, dropdowns, menus, notices and themes. Docs:
[nanoblox.com/topbarplus](https://nanoblox.com/topbarplus/).
Use it for game-wide buttons players expect up top: Shop, Inventory, Settings, Codes, Music. Keep gameplay buttons
(sprint, abilities) in your own HUD.
## Install
```toml
[dependencies]
TopbarPlus = "1foreverhd/topbarplus@3.5.0"
```
Or take the TopbarPlus model from the Creator Store, or `Icon.rbxm` from the GitHub releases, and put it in
ReplicatedStorage. The require path depends on the install; with Wally it is the package itself:
```luau
local Icon = require(game:GetService("ReplicatedStorage").Packages.TopbarPlus)
```
**Icons are client-only.** Create them in a LocalScript (or a client module) that runs once, for example from
StarterPlayerScripts.
## Basics
Every setter returns the icon, so calls chain.
```luau
local shopIcon = Icon.new()
:setName("Shop")
:setLabel("Shop")
:setImage(4882429582) -- asset id number or "rbxassetid://..." string
:setCaption("Open the shop")
:setOrder(1)
:bindToggleKey(Enum.KeyCode.B)
```
Icons appear left to right in creation order unless you use `setOrder`. `align("Left" | "Center" | "Right")` picks
the side ("Left" is the default).
## Toggling a window
An icon is either selected or deselected. Clicking toggles it, and by default selecting one icon deselects the
others (`autoDeselect`).
```luau
-- Simplest: show the frame while selected, hide it while deselected
shopIcon:bindToggleItem(shopFrame) -- a GuiObject or a ScreenGui
-- Or react yourself
shopIcon.toggled:Connect(function(isSelected, fromSource)
setShopOpen(isSelected)
end)
shopIcon.selected:Connect(function(fromSource) end)
shopIcon.deselected:Connect(function(fromSource) end)
-- bindEvent passes the icon first
shopIcon:bindEvent("deselected", function(icon) print(icon.name, "closed") end)
```
When the window can also close from inside (an X button), keep both in sync: call `shopIcon:deselect()` from the X
button, and only call `select`/`deselect` when `shopIcon.isSelected` differs from the state you want, so the two
handlers don't bounce off each other.
For a button that just does something (claim codes, open a prompt), use `:oneClick(true)`: the icon deselects
itself right after being selected, so connect to `selected`.
## Per-state looks
Toggleable setters take an optional `iconState`: `"Deselected"`, `"Selected"` or `"Viewing"` (hovered, highlighted
by a gamepad, or long-pressed on touch). Without it, the value applies to every state.
```luau
Icon.new()
:setImage(4882429582)
:setLabel("Music: On", "Deselected")
:setLabel("Music: Off", "Selected")
```
## Dropdowns, menus and notices
```luau
Icon.new()
:setLabel("More")
:setDropdown({ -- vertical list; setMenu({...}) makes a horizontal one
Icon.new():setLabel("Codes"):oneClick(true):bindEvent("selected", openCodes),
Icon.new():setLabel("Settings"):bindToggleItem(settingsFrame),
})
inventoryIcon:notify() -- adds a notice bubble (count goes up per call)
inventoryIcon:clearNotices()
```
Pass `{}` to `setDropdown` or `setMenu` to remove it. `notify(clearSignal)` clears that notice when the given
signal fires.
## Other useful methods
- `setEnabled(bool)` shows or hides the icon; `lock()` / `unlock()` stop or allow interaction.
- `setImageScale`, `setTextSize`, `setTextColor`, `setTextFont`, `setWidth`, `setCornerRadius` adjust looks.
- `modifyTheme({ "IconLabel", "TextSize", 16 })` changes any internal instance property
(`{ name, property, value, iconState }`, or an array of those). `Icon.modifyBaseTheme(...)` does it for every icon.
- `Icon.getIcon(nameOrUID)`, `Icon.getIcons()` find icons; `Icon.setTopbarEnabled(bool)` hides or shows them all.
- `destroy()` removes the icon and cleans up its connections. `addToJanitor(thing)` ties your cleanup to it.
- Read-only fields: `icon.isSelected`, `icon.isEnabled`, `icon.locked`, `icon.totalNotices`, `icon.name`.
## Respawning
Icons made by a script inside a `ResetOnSpawn` ScreenGui would be remade on every respawn, so TopbarPlus destroys the
old ones automatically (`setDestroyOnRespawn(false)` turns that off). Avoid the problem: create icons once from
StarterPlayerScripts.
## Mistakes to avoid
- Requiring TopbarPlus from a server Script. Icons only exist on the client.
- Creating icons from code that reruns (character scripts, ResetOnSpawn UI, a screen that opens repeatedly).
- Two sources of truth for "shop open". Make the icon toggle your state and your state select or deselect the icon,
with a guard against loops.
- Filling the topbar. Phones have little room; extra icons go into the overflow. Group extras in a dropdown.
- Making purchases happen on icon click without a server check. The icon only opens UI; the server validates.