---
title: Creating a custom window
summary: Build a Lua UI with a frame, table, text, button and correct cleanup.
section: ui
kind: example
status: example
tags: ["X4","modding","create","ui"]
sources: ["https://wiki.egosoft.com/X4%20Foundations%20Wiki/Modding%20Support/Breaking%20Changes/"]
---
## Choose an approach
For a small setting, consider a [settings menu API](wiki:ui-settings). To add a card or action to an existing menu, start with [extending the standard UI](wiki:extend-ui). A custom window can use Helper to create a frame, table, rows and widgets, then display it. Python and a network service are not required.
## Register Lua
Declare the Lua file in your extension's `ui.xml`, as shown below. Wait until Helper and the game UI are ready before opening the window. Loading a Lua file does not mean the world and every menu already exist.
```xml
```
## Build the window
The `ui/wiki_panel.lua` example registers its menu once. A separate event opens the panel after UI readiness. It creates a frame on layer 3, adds a table and text, binds a Close button and displays the frame. Check internal callbacks and layer usage against the current game UI.
The MD `raise_lua_event` fragment belongs inside an existing `actions` block. Your mod supplies the trigger, such as a working button or event. The example does not install a hotkey automatically.
```lua
local menu = { name = "WikiExamplePanel" }
menu.onViewCreated = function() end
menu.onRowChanged = function() end
menu.onSelectElement = function() end
function menu.onCloseElement()
if menu.shown then
Helper.clearFrame(menu, 3)
menu.shown = false
end
end
Menus = Menus or {}
table.insert(Menus, menu)
Helper.registerMenu(menu)
local function showPanel()
if menu.shown then return end
local width = Helper.scaleX(480)
local height = Helper.scaleY(150)
local frame = Helper.createFrameHandle(menu, {
layer = 3,
x = (Helper.viewWidth - width) / 2,
y = (Helper.viewHeight - height) / 2,
width = width, height = height,
standardButtons = {},
startAnimation = false,
playerControls = true,
})
local t = frame:addTable(1, { tabOrder = 1, width = width })
local title = t:addRow(false, {})
title[1]:createText("Hello, X4!", { fontsize = 14 })
local row = t:addRow(true, {})
row[1]:createButton({ height = 28 }):setText("Close")
row[1].handlers.onClick = menu.onCloseElement
menu.shown = true
frame:display()
end
RegisterEvent("Wiki.ShowPanel", function()
showPanel()
end)
```
```xml
```
## Verify the lifecycle
Repeated opening must not accumulate callbacks. Test the Close button, Escape/controller behavior, menu changes and save loading. A table with insufficient height can disappear entirely. Do not clear another menu's frame or alter global player control just to display text. This template illustrates Helper forms and has not been separately tested in game across X4 versions.