Skip to main content

EasyMacros

API for creating Macros with Easy Macros plugin.

Functions

label

widgets
EasyMacros.label(
textstring--

The text to display on the label

) → ()

Displays text

api.label("Hello, World!")

heading

widgets
EasyMacros.heading(
textstring,--

The text to display on the label

options{fontEnum.Font | Font}?
) → ()

Displays text but bigger!

Optionally, you can change the font.

-- Heading with default font
api.heading("Hello, World!")

-- Heading with custom font
api.heading("Hello, World!", {
	font = Enum.Font.SourceSansBold
})

error

widgets
EasyMacros.error(
textstring--

The error message to display.

) → ()

Displays an error message

api.error("Invalid username!")

button

widgets
EasyMacros.button(
labelstring--

The label for the button

) → ButtonHandle

A text button.

Returns a table with the following functions:

  • clicked, a function you can call to check if the button was clicked this frame
local button = api.button("Hello, World!")
if button:clicked() then
	print("Button was clicked!")
end

checkbox

widgets
EasyMacros.checkbox(
textstring,--

The label for the checkbox

options{
checkedboolean?,
disabledboolean?
}?
) → CheckboxHandle

Creates a checkbox.

Returns a table with the following functions:

  • getValue: A function to check if the checkbox is checked.
  • clicked: A function to check if the checkbox was clicked this frame.
-- We define variable outside of the render function to keep the state between render frames
-- Optionally, you can use `useState` hook to keep the state between render frames

local isChecked = false
local function render(api)
	-- Uncontrolled checkbox
	local checkbox = api.checkbox("Uncontrolled checkbox")
	if checkbox:clicked() then
		print("Checkbox was clicked, current state: " .. tostring(checkbox:getValue()))
	end

	-- Controlled checkbox
	local controlledCheckbox = api.checkbox("Controlled checkbox", {
		checked = isChecked
	})
	if controlledCheckbox:clicked() then
		isChecked = not isChecked
		print("Controlled checkbox was clicked, current state: " .. tostring(isChecked))
	end

	-- Disabled checkbox
	api.checkbox("Disabled checkbox", {
		checked = isChecked,
		disabled = true
	})
end

numberinput

widgets
EasyMacros.numberinput(
labelstring,--

The label for the input

options{
defaultstring?,
minnumber?,
maxnumber?
}?
) → NumberInputHandle

Creates an input field for numbers. Optionally, you can provide a default value and min/max constraints.

Returns a table with the following functions:

  • getValue: A function to get the current value, nil if invalid.
  • valueChanged: A function to check if the value has changed since the last frame.
-- Simple Number input
local ageInput = api.numberinput("Age")
if ageInput:valueChanged() then
	-- Print then new value
	print(ageInput:getValue())
end

-- Number input with constraints
local numberInput = api.numberinput("Radius", {
	default = 30,
	min = 0,
	max = 100,
})
if numberInput:valueChanged() then
	-- Print then new value, clamped between 0 and 100
	print(numberInput:getValue())
end

stringinput

widgets
EasyMacros.stringinput(
labelstring,--

The label for the input

options{defaultstring?}?
) → StringInputHandle

Creates an input field for text. Optionally, you can provide a default value.

Returns a table with the following functions:

  • getValue: A function to get the current value, nil if invalid.
  • valueChanged: A function to check if the value has changed since the last frame.
-- String input with no default value
local stringInput = api.stringinput("String Input")
if stringInput:valueChanged() then
	-- Print then new value
	print(stringInput:getValue())
end

-- String input with default value
local defaultStringInput = api.stringinput("String Input with default value", {
	default = "Hello, World!",
})
if defaultStringInput:valueChanged() then
	-- Print then new value
	print(defaultStringInput:getValue())
end

useState

hooks
EasyMacros.useState(
initialValueT--

The value this hook returns if the set callback has never been called

) → (
T,--

The previously set value, or the initial value if none has been set

(newValueT) → ()--

A function which when called stores the value in this hook for the next run

)

Returns a state value and an update function.

local function render(api)
	local count, setCount = api.useState(0)

	local button = api.button("Increase count")

	if button:clicked() then
		setCount(count + 1)
	end

	api.label("Count: " .. count)
end

useEffect

hooks
EasyMacros.useEffect(
callback() → () | () → () → (),--

A callback function that optionally returns a cleanup function

...any--

Dependencies

) → ()

useEffect takes a callback as a parameter which is then only invoked if passed dependencies are different from the last time this function was called. The callback is always invoked the first time this code path is reached.

If no dependencies are passed, the callback only runs once.

This function can be used to skip expensive work if none of the dependencies have changed since the last run. For example, you might use this to set a bunch of properties in a widget if any of the inputs change.

local function render(api)
	local instanceCount, setInstanceCount = api.useState(0)

	api.label("Workspace instance count: " .. instanceCount)

	-- This function will only run once when the macro is first rendered
	-- It will disconnect the event listeners when the macro is removed
	api.useEffect(function()
		local function updateInstanceCount()
			setInstanceCount(#workspace:GetDescendants())
		end

		local childAdded = workspace.ChildAdded:Connect(updateInstanceCount)
		local childRemoved = workspace.ChildRemoved:Connect(updateInstanceCount)

		return function()
			childAdded:Disconnect()
			childRemoved:Disconnect()
		end
	end)

	-- This function will run every time the instanceCount changes
	api.useEffect(function()
		print("Instance count changed to: " .. instanceCount)
	end, instanceCount)    
end

useInstance

hooks
EasyMacros.useInstance(
creator(ref{}) → ()--

A callback which creates the widget and returns it

) → Instance--

Returns the instance returned by creator

useInstance takes a callback which should be used to create the initial UI for the widget. The callback is only ever invoked on the first time this widget runs and never again. The callback should return the instance it created. The callback can optionally return a second value, which is the instance where children of this widget should be placed. Otherwise, children are placed in the first instance returned.

useInstance returns the ref table that is passed to it. You can use this to create references to objects you want to update in the widget body.

useKey

hooks
EasyMacros.useKey(keystring) → ()

Specify a key by which to store all future state in this scope. This is similar to React's key prop.

This is important to use to prevent state from one source being still being applied when it should actually reset.

create

utilities
EasyMacros.create(
classNamestring,--

The class name of the Instance to create

propsCreateProps
) → Instance--

The created instance

A function that creates an Instance tree.

CreateProps is a table:

  • String keys are interpreted as properties to set
  • children key is interpreted as an array of children to parent to the instance
  • Function values are interpreted as event handlers
  • Table keys can be used to get references to instances deep in the tree, the value becomes the key in the table

This function doesn't do anything special. It just creates an instance.

api.create("Frame", {
	BackgroundTransparency = 1,
	Name = "Checkbox",

	children = {
		api.create("TextButton", {
			BackgroundColor3 = Color3.fromRGB(54, 54, 54),
			Size = UDim2.new(0, 30, 0, 30),
			Activated = function()
				setClicked(true)
			end,

			children = {
				api.create("UICorner", {
					CornerRadius = UDim.new(0, 8),
				}),
			},
		}),
	},
})

Getting references to instances deep in a tree:

local ref = {}

api.create("Frame", {
	api.create("TextButton", {
		[ref] = "button",
		Text = "hi"
	})
})

print(ref.button.Text) --> hi

This pairs well with useInstance hook:

local function label(api, text: string)
	local ref = api.useInstance(function(refs)
		return api.create("TextLabel", {
			[ref] = "button",
		})
	end)

	ref.button.Text = text
end

widget

utilities
EasyMacros.widget(
fn(...T) → ()--

The widget function

) → (...T) → ()--

A function which can be called to create the widget

This function takes a widget function and returns a function that automatically starts a new scope when the function is called.

Here is what the label widget looks like under the hood:

return api.widget(function(text: string)
	local refs = api.useInstance(function(ref)
		api.create("TextLabel", {
			[ref] = "label",
			BackgroundTransparency = 1,
			Font = Enum.Font.SourceSans,
			TextColor3 = theme:GetColor(Enum.StudioStyleGuideColor.MainText),
			TextSize = 20,
			RichText = true,
			TextWrapped = true,
			AutomaticSize = Enum.AutomaticSize.Y,
			Size = UDim2.new(1, 0, 0, 0),
		})

		return ref.label
	end)

	refs.label.Text = tostring(text)
end)

scope

utilities
EasyMacros.scope(
fn(...T) → (),
...T--

Additional parameters to callback

) → ()

Begins a new scope. The callback is invoked immediately.

Beginning a new scope associates all further API calls with a nested scope inside this one.

Show raw api
{
    "functions": [
        {
            "name": "label",
            "desc": "Displays text\n\n```lua\napi.label(\"Hello, World!\")\n```",
            "params": [
                {
                    "name": "text",
                    "desc": "The text to display on the label",
                    "lua_type": "string"
                }
            ],
            "returns": [],
            "function_type": "static",
            "tags": [
                "widgets"
            ],
            "source": {
                "line": 21,
                "path": "src/api.lua"
            }
        },
        {
            "name": "heading",
            "desc": "Displays text but bigger!\n\nOptionally, you can change the font.\n\n```lua\n-- Heading with default font\napi.heading(\"Hello, World!\")\n\n-- Heading with custom font\napi.heading(\"Hello, World!\", {\n\tfont = Enum.Font.SourceSansBold\n})\n```",
            "params": [
                {
                    "name": "text",
                    "desc": "The text to display on the label",
                    "lua_type": "string"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "{font: Enum.Font | Font}?"
                }
            ],
            "returns": [],
            "function_type": "static",
            "tags": [
                "widgets"
            ],
            "source": {
                "line": 43,
                "path": "src/api.lua"
            }
        },
        {
            "name": "error",
            "desc": "Displays an error message\n\n```lua\napi.error(\"Invalid username!\")\n```",
            "params": [
                {
                    "name": "text",
                    "desc": "The error message to display.",
                    "lua_type": "string"
                }
            ],
            "returns": [],
            "function_type": "static",
            "tags": [
                "widgets"
            ],
            "source": {
                "line": 56,
                "path": "src/api.lua"
            }
        },
        {
            "name": "button",
            "desc": "A text button.\n\nReturns a table with the following functions:\n\n- `clicked`, a function you can call to check if the button was clicked this frame\n\n```lua\nlocal button = api.button(\"Hello, World!\")\nif button:clicked() then\n\tprint(\"Button was clicked!\")\nend\n```",
            "params": [
                {
                    "name": "label",
                    "desc": "The label for the button",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "ButtonHandle"
                }
            ],
            "function_type": "static",
            "tags": [
                "widgets"
            ],
            "source": {
                "line": 77,
                "path": "src/api.lua"
            }
        },
        {
            "name": "checkbox",
            "desc": "Creates a checkbox.\n\nReturns a table with the following functions:\n\n* `getValue`: A function to check if the checkbox is checked.\n* `clicked`: A function to check if the checkbox was clicked this frame.\n\n```lua\n-- We define variable outside of the render function to keep the state between render frames\n-- Optionally, you can use `useState` hook to keep the state between render frames\n\nlocal isChecked = false\nlocal function render(api)\n\t-- Uncontrolled checkbox\n\tlocal checkbox = api.checkbox(\"Uncontrolled checkbox\")\n\tif checkbox:clicked() then\n\t\tprint(\"Checkbox was clicked, current state: \" .. tostring(checkbox:getValue()))\n\tend\n\n\t-- Controlled checkbox\n\tlocal controlledCheckbox = api.checkbox(\"Controlled checkbox\", {\n\t\tchecked = isChecked\n\t})\n\tif controlledCheckbox:clicked() then\n\t\tisChecked = not isChecked\n\t\tprint(\"Controlled checkbox was clicked, current state: \" .. tostring(isChecked))\n\tend\n\n\t-- Disabled checkbox\n\tapi.checkbox(\"Disabled checkbox\", {\n\t\tchecked = isChecked,\n\t\tdisabled = true\n\t})\nend\n```",
            "params": [
                {
                    "name": "text",
                    "desc": "The label for the checkbox",
                    "lua_type": "string"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "{checked: boolean?, disabled: boolean?}?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "CheckboxHandle"
                }
            ],
            "function_type": "static",
            "tags": [
                "widgets"
            ],
            "source": {
                "line": 122,
                "path": "src/api.lua"
            }
        },
        {
            "name": "numberinput",
            "desc": "Creates an input field for numbers. Optionally, you can provide a default value and min/max constraints.\n\nReturns a table with the following functions:\n\n* `getValue`: A function to get the current value, nil if invalid.\n* `valueChanged`: A function to check if the value has changed since the last frame.\n\n```lua\n-- Simple Number input\nlocal ageInput = api.numberinput(\"Age\")\nif ageInput:valueChanged() then\n\t-- Print then new value\n\tprint(ageInput:getValue())\nend\n\n-- Number input with constraints\nlocal numberInput = api.numberinput(\"Radius\", {\n\tdefault = 30,\n\tmin = 0,\n\tmax = 100,\n})\nif numberInput:valueChanged() then\n\t-- Print then new value, clamped between 0 and 100\n\tprint(numberInput:getValue())\nend\n```",
            "params": [
                {
                    "name": "label",
                    "desc": "The label for the input",
                    "lua_type": "string"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "{default: string?,min: number?, max: number?}?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "NumberInputHandle"
                }
            ],
            "function_type": "static",
            "tags": [
                "widgets"
            ],
            "source": {
                "line": 158,
                "path": "src/api.lua"
            }
        },
        {
            "name": "stringinput",
            "desc": "Creates an input field for text.\nOptionally, you can provide a default value.\n\nReturns a table with the following functions:\n\n* `getValue`: A function to get the current value, nil if invalid.\n* `valueChanged`: A function to check if the value has changed since the last frame.\n\n\n```lua\n-- String input with no default value\nlocal stringInput = api.stringinput(\"String Input\")\nif stringInput:valueChanged() then\n\t-- Print then new value\n\tprint(stringInput:getValue())\nend\n\n-- String input with default value\nlocal defaultStringInput = api.stringinput(\"String Input with default value\", {\n\tdefault = \"Hello, World!\",\n})\nif defaultStringInput:valueChanged() then\n\t-- Print then new value\n\tprint(defaultStringInput:getValue())\nend\n```",
            "params": [
                {
                    "name": "label",
                    "desc": "The label for the input",
                    "lua_type": "string"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "{default: string?}?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "StringInputHandle"
                }
            ],
            "function_type": "static",
            "tags": [
                "widgets"
            ],
            "source": {
                "line": 194,
                "path": "src/api.lua"
            }
        },
        {
            "name": "useState",
            "desc": "Returns a state value and an update function.\n\n```lua\nlocal function render(api)\n\tlocal count, setCount = api.useState(0)\n\n\tlocal button = api.button(\"Increase count\")\n\n\tif button:clicked() then\n\t\tsetCount(count + 1)\n\tend\n\n\tapi.label(\"Count: \" .. count)\nend\n```",
            "params": [
                {
                    "name": "initialValue",
                    "desc": "The value this hook returns if the set callback has never been called",
                    "lua_type": "T"
                }
            ],
            "returns": [
                {
                    "desc": "The previously set value, or the initial value if none has been set",
                    "lua_type": "T"
                },
                {
                    "desc": "A function which when called stores the value in this hook for the next run",
                    "lua_type": "(newValue: T) -> ()"
                }
            ],
            "function_type": "static",
            "tags": [
                "hooks"
            ],
            "source": {
                "line": 221,
                "path": "src/api.lua"
            }
        },
        {
            "name": "useEffect",
            "desc": "`useEffect` takes a callback as a parameter which is  then only invoked if passed dependencies are different from the\nlast time this function was called. The callback is always invoked the first time this code path is reached.\n\nIf no dependencies are passed, the callback only runs once.\n\nThis function can be used to skip expensive work if none of the dependencies have changed since the last run.\nFor example, you might use this to set a bunch of properties in a widget if any of the inputs change.\n\n```lua\nlocal function render(api)\n\tlocal instanceCount, setInstanceCount = api.useState(0)\n\n\tapi.label(\"Workspace instance count: \" .. instanceCount)\n\n\t-- This function will only run once when the macro is first rendered\n\t-- It will disconnect the event listeners when the macro is removed\n\tapi.useEffect(function()\n\t\tlocal function updateInstanceCount()\n\t\t\tsetInstanceCount(#workspace:GetDescendants())\n\t\tend\n\n\t\tlocal childAdded = workspace.ChildAdded:Connect(updateInstanceCount)\n\t\tlocal childRemoved = workspace.ChildRemoved:Connect(updateInstanceCount)\n\n\t\treturn function()\n\t\t\tchildAdded:Disconnect()\n\t\t\tchildRemoved:Disconnect()\n\t\tend\n\tend)\n\n\t-- This function will run every time the instanceCount changes\n\tapi.useEffect(function()\n\t\tprint(\"Instance count changed to: \" .. instanceCount)\n\tend, instanceCount)    \nend\n```",
            "params": [
                {
                    "name": "callback",
                    "desc": "A callback function that optionally returns a cleanup function",
                    "lua_type": "() -> () | () -> () -> ()"
                },
                {
                    "name": "...",
                    "desc": "Dependencies",
                    "lua_type": "any"
                }
            ],
            "returns": [],
            "function_type": "static",
            "tags": [
                "hooks"
            ],
            "source": {
                "line": 266,
                "path": "src/api.lua"
            }
        },
        {
            "name": "useInstance",
            "desc": "`useInstance` takes a callback which should be used to create the initial UI for the widget.\nThe callback is only ever invoked on the first time this widget runs and never again.\nThe callback should return the instance it created.\nThe callback can optionally return a second value, which is the instance where children of this widget should be\nplaced. Otherwise, children are placed in the first instance returned.\n\n`useInstance` returns the `ref` table that is passed to it. You can use this to create references to objects\nyou want to update in the widget body.",
            "params": [
                {
                    "name": "creator",
                    "desc": "A callback which creates the widget and returns it",
                    "lua_type": "(ref: {}) -> (Instance, Instance?)"
                }
            ],
            "returns": [
                {
                    "desc": "Returns the instance returned by `creator`",
                    "lua_type": "Instance"
                }
            ],
            "function_type": "static",
            "tags": [
                "hooks"
            ],
            "source": {
                "line": 283,
                "path": "src/api.lua"
            }
        },
        {
            "name": "useKey",
            "desc": "Specify a key by which to store all future state in this scope. This is similar to React's `key` prop.\n\nThis is important to use to prevent state from one source being still being applied when it should actually reset.",
            "params": [
                {
                    "name": "key",
                    "desc": "",
                    "lua_type": "string"
                }
            ],
            "returns": [],
            "function_type": "static",
            "tags": [
                "hooks"
            ],
            "source": {
                "line": 294,
                "path": "src/api.lua"
            }
        },
        {
            "name": "create",
            "desc": "A function that creates an Instance tree.\n\nCreateProps is a table:\n- String keys are interpreted as properties to set\n- `children` key is interpreted as an array of children to parent to the instance\n- Function values are interpreted as event handlers\n- Table keys can be used to get references to instances deep in the tree, the value becomes the key in the table\n\nThis function doesn't do anything special. It just creates an instance.\n\n```lua\napi.create(\"Frame\", {\n\tBackgroundTransparency = 1,\n\tName = \"Checkbox\",\n\n\tchildren = {\n\t\tapi.create(\"TextButton\", {\n\t\t\tBackgroundColor3 = Color3.fromRGB(54, 54, 54),\n\t\t\tSize = UDim2.new(0, 30, 0, 30),\n\t\t\tActivated = function()\n\t\t\t\tsetClicked(true)\n\t\t\tend,\n\n\t\t\tchildren = {\n\t\t\t\tapi.create(\"UICorner\", {\n\t\t\t\t\tCornerRadius = UDim.new(0, 8),\n\t\t\t\t}),\n\t\t\t},\n\t\t}),\n\t},\n})\n```\n\nGetting references to instances deep in a tree:\n\n```lua\nlocal ref = {}\n\napi.create(\"Frame\", {\n\tapi.create(\"TextButton\", {\n\t\t[ref] = \"button\",\n\t\tText = \"hi\"\n\t})\n})\n\nprint(ref.button.Text) --> hi\n```\n\nThis pairs well with useInstance hook:\n\n```lua\nlocal function label(api, text: string)\n\tlocal ref = api.useInstance(function(refs)\n\t\treturn api.create(\"TextLabel\", {\n\t\t\t[ref] = \"button\",\n\t\t})\n\tend)\n\n\tref.button.Text = text\nend\n\n```",
            "params": [
                {
                    "name": "className",
                    "desc": "The class name of the Instance to create",
                    "lua_type": "string"
                },
                {
                    "name": "props",
                    "desc": "",
                    "lua_type": "CreateProps"
                }
            ],
            "returns": [
                {
                    "desc": "The created instance",
                    "lua_type": "Instance"
                }
            ],
            "function_type": "static",
            "tags": [
                "utilities"
            ],
            "source": {
                "line": 368,
                "path": "src/api.lua"
            }
        },
        {
            "name": "widget",
            "desc": "This function takes a widget function and returns a function that automatically starts a new scope when the function\nis called.\n\nHere is what the `label` widget looks like under the hood:\n\n```lua\nreturn api.widget(function(text: string)\n\tlocal refs = api.useInstance(function(ref)\n\t\tapi.create(\"TextLabel\", {\n\t\t\t[ref] = \"label\",\n\t\t\tBackgroundTransparency = 1,\n\t\t\tFont = Enum.Font.SourceSans,\n\t\t\tTextColor3 = theme:GetColor(Enum.StudioStyleGuideColor.MainText),\n\t\t\tTextSize = 20,\n\t\t\tRichText = true,\n\t\t\tTextWrapped = true,\n\t\t\tAutomaticSize = Enum.AutomaticSize.Y,\n\t\t\tSize = UDim2.new(1, 0, 0, 0),\n\t\t})\n\n\t\treturn ref.label\n\tend)\n\n\trefs.label.Text = tostring(text)\nend)\n```",
            "params": [
                {
                    "name": "fn",
                    "desc": "The widget function",
                    "lua_type": "(...: T) -> ()"
                }
            ],
            "returns": [
                {
                    "desc": "A function which can be called to create the widget",
                    "lua_type": "(...: T) -> ()"
                }
            ],
            "function_type": "static",
            "tags": [
                "utilities"
            ],
            "source": {
                "line": 403,
                "path": "src/api.lua"
            }
        },
        {
            "name": "scope",
            "desc": "Begins a new scope.\nThe `callback` is invoked immediately.\n\nBeginning a new scope associates all further API calls with a nested scope inside this one.",
            "params": [
                {
                    "name": "fn",
                    "desc": "",
                    "lua_type": "(...: T) -> ()"
                },
                {
                    "name": "...",
                    "desc": "Additional parameters to `callback`",
                    "lua_type": "T"
                }
            ],
            "returns": [],
            "function_type": "static",
            "tags": [
                "utilities"
            ],
            "source": {
                "line": 416,
                "path": "src/api.lua"
            }
        }
    ],
    "properties": [],
    "types": [],
    "name": "EasyMacros",
    "desc": "API for creating Macros with Easy Macros plugin.",
    "source": {
        "line": 6,
        "path": "src/api.lua"
    }
}