For those who’d prefer, a version of the article is available on Medium.
I’ve got tired of having to deal with the imperative plugin APIs, so I created this open-source package to tightly integrate React to Roblox Studio plugin development. If you are already using React for plugin development, you will find this package very useful to eliminate a large portion of the bloat that comes with the initial plugin setup.
Why React for Roblox Plugins?
- Build plugins declaratively: easily manage plugin widget states, toolbars, buttons and actions.
- If you are already using React for other projects, you can re-use your generic components for plugin development.
- Gain access to great community built React libraries like:
- Studio Components, a library of UI components that matches Roblox Studio (synchronized with the current theme)
- react-lua-hooks and react-roblox-hooks, two collections of hooks for React
Getting Started
Plugin Main Script
The main script will look very similar to any plugin built with React. As usual, it will use react-roblox
to create a root and render your main component.
All that is needed is to wrap your main component with the RobloxPlugin
provider.
local React = require('@pkg/@jsdotlua/react')
local ReactRoblox = require('@pkg/@jsdotlua/react-roblox')
local ReactRobloxStudioPlugin = require('@pkg/@seaofvoices/react-roblox-studio-plugin')
local PluginApp = require('./PluginApp')
local RobloxPlugin = ReactRobloxStudioPlugin.RobloxPlugin
local container = Instance.new('Folder')
container.Name = 'PluginView'
local root = ReactRoblox.createRoot(container)
local element = React.createElement(
RobloxPlugin,
{ plugin = plugin },
React.createElement(PluginApp)
)
root:render(element, container)
Create a Button Inside a Toolbar
Now let’s look at an example of how the PluginApp
component could look. In this example, the plugin will simply create a toolbar and a button.
`
local React = require('@pkg/@jsdotlua/react')
local ReactRobloxStudioPlugin = require('@pkg/@seaofvoices/react-roblox-studio-plugin')
local Toolbar = ReactRobloxStudioPlugin.Toolbar
local ToolbarButton = ReactRobloxStudioPlugin.ToolbarButton
local function PluginApp()
return React.createElement(
Toolbar,
{ name = "Toolbar Example" },
React.createElement(ToolbarButton, {
id = "first-button",
text = "Click",
onClick = function()
print("Button clicked!")
end,
})
)
end
return PluginApp
Create a Plugin Widget
Let’s push things a little further and add a DockWidgetPluginGui
connected to a button inside a toolbar.
local React = require('@pkg/@jsdotlua/react')
local ReactRobloxStudioPlugin = require('@pkg/@seaofvoices/react-roblox-studio-plugin')
local ReactLuaHooks = require('@pkg/@seaofvoices/react-lua-hooks')
local useToggle = ReactLuaHooks.useToggle
local Toolbar = ReactRobloxStudioPlugin.Toolbar
local ToolbarButton = ReactRobloxStudioPlugin.ToolbarButton
local Widget = ReactRobloxStudioPlugin.Widget
local function PluginApp()
-- useToggle is simply a tiny wrapper around a useState
-- from https://github.com/seaofvoices/react-lua-hooks
-- that gives a toggle, on and off functions for a boolean
local widgetEnabled, setWidgetState = useToggle(false)
return React.createElement(
React.Fragment,
nil,
React.createElement(
Toolbar,
{ name = "Toolbar Example" },
React.createElement(ToolbarButton, {
id = "toggle-widget",
text = if widgetEnabled then "Open Widget" else "Close Widget",
onClick = setWidgetState.toggle,
})
),
React.createElement(
Widget,
{
id = "example-widget",
title = "Example Widget",
enabled = widgetEnabled,
minSize = Vector2.new(200, 200),
initialDockState = Enum.InitialDockState.Float,
onClose = setWidgetState.off,
},
React.createElement("TextLabel", {
Text = "This is a widget!",
Size = UDim2.new(1, 0, 0, 30),
BackgroundTransparency = 1,
})
)
)
end
return PluginApp
Access the plugin
Global
At some point, you may need to call some plugin specific functions from the plugin
global variable (like plugin:GetMouse()
or plugin:OpenScript()
). To do that, use the usePlugin
hook:
local React = require('@pkg/@jsdotlua/react')
local ReactRobloxStudioPlugin = require('@pkg/@seaofvoices/react-roblox-studio-plugin')
local usePlugin = ReactRobloxStudioPlugin.usePlugin
local function ComponentExample(props)
local plugin = usePlugin()
-- ...
end
API Reference
There is even more to the library! I invite you to visit the components and hooks reference on GitHub to learn more.
Installation
This library is available on npm:
npm install @seaofvoices/react-roblox-studio-plugin
# or if you are using yarn
yarn add @seaofvoices/react-roblox-studio-plugin
The easiest way to get started with npm is with generator-luau
, a Luau project generator that creates all the necessary configuration files to have luau-lsp
, selene
, stylua
, darklua
and even jest-lua. You can learn more about this in this article.
Feedback
If you have any suggestions of improvements or issues to report, feel free to visit the GitHub issues list. Create a new issue, comment or add a reaction to an existing one to show your interests.
For broader discussion or questions, you can of course reply to this post or catch me in the Roblox open-source Discord server.
End Notes
I work part time to dedicate time to the Luau open-source ecosystem. If you find this library or my other projects helpful, please consider buying me a coffee on ko-fi
I would love to see your plugins created with this library. It’s always great and encouraging to see what the community builds!
To learn more about Luau on npm: