Theming
Every colour, radius, blur and duration in of_ui is a token. A full set of tokens exports as one short code (OF1-...) that you can pin as your server's default, offer as a preset, or paste into another server.
The settings menu
Players open it with:
/themesettingsThey can also import a code straight away, skipping the menu:
/themesettings OF1-fWNj4N8...Codes are validated and checksummed on import, so a bad paste is rejected rather than applied.
A player's theme is stored per client and survives resource restarts.
Configuration
config/config.lua:
Config.Theme = {
-- Server default. Players start from this one; Reset returns them to it.
Default = nil, -- e.g. 'OF1-fWNj/4N8...'
-- Let players open the menu and keep their own theme.
AllowPlayerTheming = true,
-- Tokens the player may not change, by registry path.
LockedTokens = {},
-- Themes offered as one-click chips in the menu.
Presets = {
-- { label = 'Midnight', code = 'OF1-...' },
},
-- Sounds offered in the Notify section. Files live in sounds/.
NotifySounds = {
-- { label = 'Ping', url = 'https://cfx-nui-of_ui/sounds/ping.ogg' },
},
-- Chat command that opens the menu. false for none.
Command = 'themesettings',
-- Keybind that opens the menu. false for none.
Keybind = false,
}| Field | Type | Description |
|---|---|---|
Default | string? | Theme code every player starts from. nil uses the built-in defaults. |
AllowPlayerTheming | boolean? | false locks the theme to Default and blocks saving. |
LockedTokens | string[]? | Token paths the player cannot change, e.g. { 'global.accent', 'global.accentHover' }. |
Presets | table[]? | { label, code } chips shown in the menu. |
NotifySounds | table[]? | { label, url } sounds players can pick for their notifications. See below. |
Command | string|false? | Chat command. Omit it entirely and it falls back to themesettings. |
Keybind | string|false? | Default key for opening the menu. |
Pinning your branding
To keep your server's accent colour while letting players change everything else:
Config.Theme = {
Default = 'OF1-...', -- your theme code
AllowPlayerTheming = true,
LockedTokens = { 'global.accent', 'global.accentHover' },
}Notification sounds
Drop an audio file in the resource's sounds/ folder and register it:
Config.Theme = {
NotifySounds = {
{ label = 'Ping', url = 'https://cfx-nui-of_ui/sounds/ping.ogg' },
},
}label is the name in the picker, url points at the file — the https://cfx-nui-of_ui/sounds/<file>
form is served by the resource itself, so it works offline. A CDN link works too. Name files
without spaces; FiveM's NUI server can't serve a path that has one.
.mp3, .ogg, .wav and .flac play everywhere; .m4a/.aac/.opus/.webm are accepted but
worth testing, since a format the client can't decode is silently skipped.
Each player then picks one under Notify → Sound (with None as the default) and sets Volume next to it; the play button previews it. Both are part of the theme code, so a server default can ship with a sound already selected.
A notification that passes its own sound = { set, name } plays that game sound instead — the
player's notification sound doesn't stack on top of it.
Making a theme
- Open
/themesettingsin game. - Adjust tokens; the preview updates as you go.
- Copy the theme code from the export field.
- Paste it into
Config.Theme.Default, or intoPresetsas a one-click chip.
Updating
Theme codes are versioned, and they survive updates that only add tokens.
An update that adds a component or an option reads your existing code as it was written and leaves the new tokens on their defaults, so your Default, your presets and every player's saved theme keep working — codes from v1.2.0 onwards included.
An update that reworks existing tokens can't do that: those codes are refused, your server falls back to the built-in theme and players fall back to your Default. Nothing is deleted — a player's code stays in their client storage until they save again — but you'd have to rebuild the theme in the menu. The changelog says so explicitly when it happens; check it before updating a live server.
Per-call overrides
Some components take their own colour without touching the theme, for cases where the meaning is specific rather than stylistic:
