Registers (or updates) a widget in the phone gallery.
Recommended pattern (wait until gksphone is started):
RemoveCustomWidget
Removes a widget from the gallery registry. Home-screen instances of that id are cleaned up by the phone.
Example on resource stop:
If your widgetUrl uses https://cfx-nui-<resource>/..., GKSPHONE also auto-removes matching widgets when that resource stops.
Widget sizes
Size
Cells
Notes
1x1
1Γ1
Compact
2x2
2Γ2
Default (recommended for demos)
4x2
Full width Γ 2 rows
Wide strip
4x4
Full width Γ 4 rows
Large
The size is stored on each home-screen instance when the player adds the widget from the gallery.
Javascript UI
The phone embeds your page in an iframe and sends postMessage events.
Init message
Sent when the iframe finishes loading:
Editing message
Sent when the player enters or leaves home edit mode:
While editing, the phone disables pointer events on the iframe (same jiggle behavior as built-in widgets). Use the editing flag if you want to show an in-widget hint.
Minimal HTML example
Config (optional)
You can also list static widgets in gksphone config:
Runtime registration via AddCustomWidget is preferred for third-party resources.
local widgetData = {
id = "demo-widget", --- A unique id (required)
widgetUrl = "https://cfx-nui-custom-widget/ui/widget.html", -- iframe URL (required)
title = "Demo Widget", -- Gallery title (fallback if labelLangs missing)
description = "Example custom home widget", -- Gallery subtitle
icon = "sparkles", -- Framework7 icon name used in the gallery
size = "2x2", -- "1x1" | "2x2" | "4x2" | "4x4" (default: "2x2")
show = true, -- Show in widget gallery (default: true)
labelLangs = { -- Widget name by languages
af = "Demo Widget",
ar = "Demo Widget",
cs = "Demo Widget",
de = "Demo Widget",
en = "Demo Widget",
es = "Demo Widget",
fr = "Demo Widget",
id = "Demo Widget",
nl = "Demo Widget",
["pt-PT"] = "Demo Widget",
ro = "Demo Widget",
sv = "Demo Widget",
th = "Demo Widget",
tr = "Demo Widget",
uk = "Demo Widget",
["zh-TW"] = "Demo Widget"
}
}
exports["gksphone"]:AddCustomWidget(widgetData)
local WIDGET_ID = "demo-widget"
local RESOURCE = GetCurrentResourceName()
local function registerWidget()
if GetResourceState("gksphone") ~= "started" then
return false
end
return exports["gksphone"]:AddCustomWidget({
id = WIDGET_ID,
widgetUrl = ("https://cfx-nui-%s/ui/widget.html"):format(RESOURCE),
title = "Demo Widget",
description = "Example custom home widget",
icon = "sparkles",
size = "2x2",
show = true,
labelLangs = {
en = "Demo Widget",
tr = "Demo Widget"
}
}) == true
end
CreateThread(function()
local tries = 0
while tries < 60 do
if registerWidget() then
return
end
tries = tries + 1
Wait(1000)
end
end)
AddEventHandler("onResourceStart", function(resourceName)
if resourceName == "gksphone" then
Wait(500)
registerWidget()
end
end)
AddEventHandler("onResourceStop", function(resourceName)
if resourceName ~= GetCurrentResourceName() then return end
if GetResourceState("gksphone") == "started" then
pcall(function()
exports["gksphone"]:RemoveCustomWidget("demo-widget")
end)
end
end)
window.addEventListener("message", (event) => {
const data = event.data
if (!data || typeof data !== "object") return
if (data.type === "gksphone:widget:init") {
// data.widgetId β registry id
// data.size β "2x2" | "4x2" | ...
// data.editing β true while home edit mode is active
console.log("Widget init", data.widgetId, data.size, data.editing)
return
}
if (data.type === "gksphone:widget:editing") {
// data.editing β edit mode toggled
console.log("Editing", data.editing)
}
})