--[[ MIT License Copyright (c) 2026 sillzchibi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]]-- local UserInputService = game:GetService("UserInputService") local GuiService = game:GetService("GuiService") local TweenService = game:GetService("TweenService") local RunService = game:GetService("RunService") local Packages = game:GetService("CorePackages").Packages local CoreGui = game:GetService("CoreGui") -- // CONFIGURATION // local React = require(Packages.React) local RbxReact = require(Packages.ReactRoblox) local MIN_WIDTH = 300 local MIN_HEIGHT = 200 local THEMES = { Color3.fromRGB(20, 20, 20), Color3.fromRGB(180, 40, 40), Color3.fromRGB(120, 75, 255), Color3.fromRGB(30, 80, 160), Color3.fromRGB(40, 140, 70), } local TITLE = "ReactHub ⚛️" local FONT = Font.fromEnum(Enum.Font.GothamBold) local UI_ELEMENTS = { { Type = "Switch", Text = "Auto Farm", Action = function(isOn) print("Auto Farm: " .. tostring(isOn)) end }, { Type = "Switch", Text = "ESP Visuals", Action = function(isOn) print("ESP: " .. tostring(isOn)) end }, { Type = "Button", Text = "Unlock All Doors", ButtonText = "Execute", Action = function() print("Unlocked") end }, { Type = "Button", Text = "Heal Character", ButtonText = "Heal", Action = function() print("Healed") end }, { Type = "TextBox", Text = "Target Player", Action = function(text) print("Target set to: " .. text) end }, } -- // COMPONENTS // local function SafeExecute(actionType, actionText, func, ...) if not func then return end local success, err = pcall(func, ...) if not success then warn(string.format( '[ ReactUI ]: Failed to run %s - "%s" reason: %s\nthis happened because one of your [Action = function] failed/errored', actionType, actionText, tostring(err) )) end end local function ToggleSwitch(props) local isOn, setIsOn = React.useState(false) local circleRef = React.useRef(nil) local bgRef = React.useRef(nil) local tweens = React.useRef({}) React.useEffect(function() if not circleRef.current or not bgRef.current then return end if not tweens.current.on then local info = TweenInfo.new(0.2, Enum.EasingStyle.Back, Enum.EasingDirection.Out) tweens.current.on = TweenService:Create(circleRef.current, info, {Position = UDim2.new(1, -22, 0.5, 0)}) tweens.current.off = TweenService:Create(circleRef.current, info, {Position = UDim2.new(0, 2, 0.5, 0)}) tweens.current.bgOn = TweenService:Create(bgRef.current, info, {BackgroundColor3 = Color3.fromRGB(10, 132, 255)}) tweens.current.bgOff = TweenService:Create(bgRef.current, info, {BackgroundColor3 = Color3.fromRGB(60, 60, 60)}) end if isOn then tweens.current.on:Play() tweens.current.bgOn:Play() else tweens.current.off:Play() tweens.current.bgOff:Play() end end, {isOn}) return React.createElement("Frame", { LayoutOrder = props.LayoutOrder, BackgroundColor3 = Color3.fromRGB(30, 30, 30), Size = UDim2.new(1, 0, 0, 55), }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(0, 12) }), Stroke = React.createElement("UIStroke", { Color = Color3.fromRGB(50, 50, 50), Thickness = 1 }), Label = React.createElement("TextLabel", { BackgroundTransparency = 1, Size = UDim2.new(1, -80, 1, 0), Position = UDim2.new(0, 15, 0, 0), Text = props.Text, TextColor3 = Color3.new(1, 1, 1), TextSize = 18, FontFace = FONT, TextXAlignment = Enum.TextXAlignment.Left, }), Switch = React.createElement("TextButton", { Size = UDim2.new(0, 44, 0, 24), Position = UDim2.new(1, -15, 0.5, 0), AnchorPoint = Vector2.new(1, 0.5), BackgroundColor3 = Color3.fromRGB(60, 60, 60), Text = "", ref = bgRef, [React.Event.Activated] = function() local newState = not isOn setIsOn(newState) SafeExecute("Switch", props.Text, props.Action, newState) end, }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(1, 0) }), Indicator = React.createElement("Frame", { Size = UDim2.new(0, 20, 0, 20), Position = UDim2.new(0, 2, 0.5, 0), AnchorPoint = Vector2.new(0, 0.5), BackgroundColor3 = Color3.new(1, 1, 1), ref = circleRef, }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(1, 0) }) }) }) }) end local function ActionButton(props) local buttonRef = React.useRef(nil) local tweens = React.useRef({}) local function animate(scale) if not buttonRef.current then return end if not tweens.current.down then local info = TweenInfo.new(0.15, Enum.EasingStyle.Quad, Enum.EasingDirection.Out) tweens.current.down = TweenService:Create(buttonRef.current, info, { Size = UDim2.new(0.9, 0, 0.9, 0) }) tweens.current.up = TweenService:Create(buttonRef.current, info, { Size = UDim2.new(1, 0, 1, 0) }) end if scale == 0 then tweens.current.down:Play() else tweens.current.up:Play() end end return React.createElement("Frame", { LayoutOrder = props.LayoutOrder, BackgroundColor3 = Color3.fromRGB(30, 30, 30), Size = UDim2.new(1, 0, 0, 55), }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(0, 12) }), Stroke = React.createElement("UIStroke", { Color = Color3.fromRGB(50, 50, 50), Thickness = 1 }), Label = React.createElement("TextLabel", { BackgroundTransparency = 1, Size = UDim2.new(1, -110, 1, 0), Position = UDim2.new(0, 15, 0, 0), Text = props.Text, TextColor3 = Color3.new(1, 1, 1), TextSize = 18, FontFace = FONT, TextXAlignment = Enum.TextXAlignment.Left, }), ButtonContainer = React.createElement("Frame", { BackgroundTransparency = 1, Size = UDim2.new(0, 80, 0, 32), Position = UDim2.new(1, -15, 0.5, 0), AnchorPoint = Vector2.new(1, 0.5), }, { Button = React.createElement("TextButton", { Size = UDim2.new(1, 0, 1, 0), Position = UDim2.new(0.5, 0, 0.5, 0), AnchorPoint = Vector2.new(0.5, 0.5), BackgroundColor3 = props.ThemeColor, Text = props.ButtonText or "Click", TextColor3 = Color3.new(1, 1, 1), TextSize = 14, FontFace = FONT, AutoButtonColor = false, ref = buttonRef, [React.Event.InputBegan] = function(_, input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then animate(0) end end, [React.Event.InputEnded] = function(_, input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then animate(1) end end, [React.Event.Activated] = function() SafeExecute("Button", props.Text, props.Action) end, }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(0, 6) }), }) }) }) end local function InputTextBox(props) return React.createElement("Frame", { LayoutOrder = props.LayoutOrder, BackgroundColor3 = Color3.fromRGB(30, 30, 30), Size = UDim2.new(1, 0, 0, 55), }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(0, 12) }), Stroke = React.createElement("UIStroke", { Color = Color3.fromRGB(50, 50, 50), Thickness = 1 }), Label = React.createElement("TextLabel", { BackgroundTransparency = 1, Size = UDim2.new(0.4, 0, 1, 0), Position = UDim2.new(0, 15, 0, 0), Text = props.Text, TextColor3 = Color3.new(1, 1, 1), TextSize = 18, FontFace = FONT, TextXAlignment = Enum.TextXAlignment.Left, }), Input = React.createElement("TextBox", { Size = UDim2.new(0.6, -30, 0, 35), Position = UDim2.new(1, -15, 0.5, 0), AnchorPoint = Vector2.new(1, 0.5), BackgroundColor3 = Color3.fromRGB(45, 45, 45), Text = "", PlaceholderText = "...", TextColor3 = Color3.new(1, 1, 1), PlaceholderColor3 = Color3.fromRGB(150, 150, 150), TextSize = 14, Font = Enum.Font.GothamMedium, --// textbox font doesn't follow the FONT so feel free to change this TextWrapped = true, ClearTextOnFocus = false, [React.Event.FocusLost] = function(rbx) SafeExecute("TextBox", props.Text, props.Action, rbx.Text) end, }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(0, 8) }), Stroke = React.createElement("UIStroke", { Color = Color3.fromRGB(70, 70, 70), Thickness = 1 }), }) }) end local function CreateReactUI() local isVisible, setIsVisible = React.useState(true) local isMinimized, setIsMinimized = React.useState(false) local isMaximized, setIsMaximized = React.useState(false) local themeIndex, setThemeIndex = React.useState(1) local isMinimizedRef = React.useRef(isMinimized) local position, setPosition = React.useBinding(UDim2.new(0, 100, 0, 100)) local size, setSize = React.useBinding(UDim2.new(0, 400, 0, 300)) local savedNormalSize = React.useRef(UDim2.new(0, 400, 0, 300)) local savedNormalPos = React.useRef(UDim2.new(0, 100, 0, 100)) local preMinSize = React.useRef(UDim2.new(0, 400, 0, 300)) local gearRef = React.useRef(nil) local rootRef = React.useRef(nil) local dragStart = React.useRef(nil) local startAbsPos = React.useRef(Vector2.new(0, 0)) local startAbsSize = React.useRef(Vector2.new(0, 0)) local dragType = React.useRef(nil) local tweenConn = React.useRef(nil) React.useEffect(function() isMinimizedRef.current = isMinimized end, {isMinimized}) local function animateWindow(targetSize, targetPos, duration) if tweenConn.current then tweenConn.current:Disconnect() end local startT = tick() local startSizeVal = size:getValue() local startPosVal = position:getValue() duration = duration or 0.25 tweenConn.current = RunService.RenderStepped:Connect(function() local alpha = math.clamp((tick() - startT) / duration, 0, 1) local easeAlpha = 1 - math.pow(1 - alpha, 3) if targetSize then setSize(startSizeVal:Lerp(targetSize, easeAlpha)) end if targetPos then setPosition(startPosVal:Lerp(targetPos, easeAlpha)) end if alpha >= 1 then tweenConn.current:Disconnect() tweenConn.current = nil end end) end local function onHandleDown(actionType) return function(rbx, input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then if not rootRef.current then return end local absPos = rootRef.current.AbsolutePosition setPosition(UDim2.new(0, absPos.X, 0, absPos.Y)) dragStart.current = input.Position startAbsPos.current = absPos startAbsSize.current = rootRef.current.AbsoluteSize dragType.current = actionType if tweenConn.current then tweenConn.current:Disconnect() tweenConn.current = nil end end end end React.useEffect(function() local menuConn = GuiService.MenuOpened:Connect(function() setIsVisible(true) end) local inputChanged = UserInputService.InputChanged:Connect(function(input) if not dragStart.current then return end if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then local delta = input.Position - dragStart.current local sSize = startAbsSize.current local sPos = startAbsPos.current local dt = dragType.current local viewport = workspace.CurrentCamera.ViewportSize local newW = sSize.X local newH = sSize.Y local newX = sPos.X local newY = sPos.Y if string.find(dt, "Right") then newW = math.clamp(sSize.X + delta.X, MIN_WIDTH, viewport.X - sPos.X) elseif string.find(dt, "Left") then newW = math.clamp(sSize.X - delta.X, MIN_WIDTH, sSize.X + sPos.X) newX = sPos.X + (sSize.X - newW) end if not isMinimizedRef.current then if string.find(dt, "Bottom") then newH = math.clamp(sSize.Y + delta.Y, MIN_HEIGHT, viewport.Y - sPos.Y) elseif string.find(dt, "Top") then newH = math.clamp(sSize.Y - delta.Y, MIN_HEIGHT, sSize.Y + sPos.Y) newY = sPos.Y + (sSize.Y - newH) end end setSize(UDim2.new(0, newW, 0, newH)) setPosition(UDim2.new(0, newX, 0, newY)) end end) local inputEnded = UserInputService.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragStart.current = nil end end) return function() menuConn:Disconnect() inputChanged:Disconnect() inputEnded:Disconnect() end end, {}) local function cycleTheme() if gearRef.current then TweenService:Create(gearRef.current, TweenInfo.new(0.5, Enum.EasingStyle.Back), {Rotation = gearRef.current.Rotation + 360}):Play() end setThemeIndex((themeIndex % #THEMES) + 1) end local function toggleMaximize() local currentPos = rootRef.current and UDim2.new(0, rootRef.current.AbsolutePosition.X, 0, rootRef.current.AbsolutePosition.Y) or position:getValue() if not isMaximized then if not isMinimized then savedNormalSize.current = size:getValue() savedNormalPos.current = currentPos end local viewport = workspace.CurrentCamera.ViewportSize local targetW = viewport.X - 40 local targetH = viewport.Y - 40 animateWindow(UDim2.new(0, targetW, 0, targetH), UDim2.new(0, 20, 0, 20)) setIsMaximized(true) setIsMinimized(false) else animateWindow(savedNormalSize.current, savedNormalPos.current) setIsMaximized(false) setIsMinimized(false) end end local function toggleMinimize() local currentPos = rootRef.current and UDim2.new(0, rootRef.current.AbsolutePosition.X, 0, rootRef.current.AbsolutePosition.Y) or position:getValue() setPosition(currentPos) if not isMinimized then preMinSize.current = size:getValue() animateWindow(UDim2.new(0, preMinSize.current.X.Offset, 0, 40), nil) setIsMinimized(true) else animateWindow(preMinSize.current, nil) setIsMinimized(false) end end if not isVisible then return nil end local function createHandles() if isMaximized then return {} end local handles = { Top = { Size = UDim2.new(1, -32, 0, 8), Pos = UDim2.new(0.5, 0, 0, 0), AP = Vector2.new(0.5, 0) }, Bottom = { Size = UDim2.new(1, -32, 0, 8), Pos = UDim2.new(0.5, 0, 1, 0), AP = Vector2.new(0.5, 1) }, Left = { Size = UDim2.new(0, 8, 1, -32), Pos = UDim2.new(0, 0, 0.5, 0), AP = Vector2.new(0, 0.5) }, Right = { Size = UDim2.new(0, 8, 1, -32), Pos = UDim2.new(1, 0, 0.5, 0), AP = Vector2.new(1, 0.5) }, TopLeft = { Size = UDim2.new(0, 16, 0, 16), Pos = UDim2.new(0, 0, 0, 0), AP = Vector2.new(0, 0) }, TopRight = { Size = UDim2.new(0, 16, 0, 16), Pos = UDim2.new(1, 0, 0, 0), AP = Vector2.new(1, 0) }, BottomLeft = { Size = UDim2.new(0, 16, 0, 16), Pos = UDim2.new(0, 0, 1, 0), AP = Vector2.new(0, 1) }, BottomRight = { Size = UDim2.new(0, 16, 0, 16), Pos = UDim2.new(1, 0, 1, 0), AP = Vector2.new(1, 1) }, } local elements = {} for name, data in handles do elements["Handle_"..name] = React.createElement("TextButton", { Size = data.Size, Position = data.Pos, AnchorPoint = data.AP, BackgroundTransparency = 1, Text = "", ZIndex = 10, [React.Event.InputBegan] = onHandleDown(name), }) end return elements end local activeTheme = THEMES[themeIndex] local childrenList = { Layout = React.createElement("UIListLayout", { SortOrder = Enum.SortOrder.LayoutOrder, Padding = UDim.new(0, 10) }), Padding = React.createElement("UIPadding", { PaddingTop = UDim.new(0, 15), PaddingLeft = UDim.new(0, 15), PaddingRight = UDim.new(0, 15), PaddingBottom = UDim.new(0, 15) }), } for i, v in ipairs(UI_ELEMENTS) do if v.Type == "Switch" then childrenList["Element_"..i] = React.createElement(ToggleSwitch, { LayoutOrder = i, Text = v.Text, Action = v.Action, ThemeColor = activeTheme }) elseif v.Type == "Button" then childrenList["Element_"..i] = React.createElement(ActionButton, { LayoutOrder = i, Text = v.Text, ButtonText = v.ButtonText, Action = v.Action, ThemeColor = activeTheme }) elseif v.Type == "TextBox" then childrenList["Element_"..i] = React.createElement(InputTextBox, { LayoutOrder = i, Text = v.Text, Action = v.Action }) end end local outerChildren = { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(0, 18) }), Stroke = React.createElement("UIStroke", { Color = Color3.new(1, 1, 1), Transparency = 0.8, Thickness = 1.5 }), Main = React.createElement("Frame", { Size = UDim2.new(1, -12, 1, -12), Position = UDim2.new(0.5, 0, 0.5, 0), AnchorPoint = Vector2.new(0.5, 0.5), BackgroundColor3 = activeTheme, BorderSizePixel = 0, ClipsDescendants = true, }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(0, 12) }), TopBar = React.createElement("Frame", { Size = UDim2.new(1, 0, 0, 40), BackgroundTransparency = 1, ZIndex = 2, }, { Title = React.createElement("TextLabel", { BackgroundTransparency = 1, Size = UDim2.new(1, -120, 1, 0), Position = UDim2.new(0, 45, 0, 0), Text = TITLE, TextColor3 = Color3.new(1, 1, 1), FontFace = FONT, TextSize = 15, TextXAlignment = Enum.TextXAlignment.Left, }), Gear = React.createElement("TextButton", { Size = UDim2.new(0, 25, 0, 25), Position = UDim2.new(0, 12, 0.5, 0), AnchorPoint = Vector2.new(0, 0.5), BackgroundTransparency = 1, Text = "⚙️", TextSize = 20, ref = gearRef, [React.Event.Activated] = cycleTheme, }), Controls = React.createElement("Frame", { Size = UDim2.new(0, 90, 1, 0), Position = UDim2.new(1, -10, 0, 0), AnchorPoint = Vector2.new(1, 0), BackgroundTransparency = 1, }, { Layout = React.createElement("UIListLayout", { FillDirection = Enum.FillDirection.Horizontal, HorizontalAlignment = Enum.HorizontalAlignment.Right, VerticalAlignment = Enum.VerticalAlignment.Center, Padding = UDim.new(0, 10), SortOrder = Enum.SortOrder.LayoutOrder, }), Green = React.createElement("TextButton", { LayoutOrder = 1, Size = UDim2.new(0, 18, 0, 18), BackgroundColor3 = Color3.fromRGB(40, 200, 64), Text = "", [React.Event.Activated] = toggleMaximize, }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(1, 0) }) }), Yellow = React.createElement("TextButton", { LayoutOrder = 2, Size = UDim2.new(0, 18, 0, 18), BackgroundColor3 = Color3.fromRGB(255, 189, 46), Text = "", [React.Event.Activated] = toggleMinimize, }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(1, 0) }) }), Red = React.createElement("TextButton", { LayoutOrder = 3, Size = UDim2.new(0, 18, 0, 18), BackgroundColor3 = Color3.fromRGB(255, 95, 86), Text = "", [React.Event.Activated] = function() setIsVisible(false) end, }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(1, 0) }) }), }) }), ContentContainer = React.createElement("Frame", { Size = UDim2.new(1, 0, 1, -40), Position = UDim2.new(0, 0, 0, 40), BackgroundColor3 = Color3.new(0, 0, 0), BorderSizePixel = 0, Visible = not isMinimized, }, { Corner = React.createElement("UICorner", { CornerRadius = UDim.new(0, 12) }), List = React.createElement("ScrollingFrame", { Size = UDim2.new(1, 0, 1, 0), BackgroundTransparency = 1, BorderSizePixel = 0, ScrollBarThickness = 2, CanvasSize = UDim2.new(0, 0, 0, #UI_ELEMENTS * 65 + 30), }, childrenList) }) }) } for k, v in createHandles() do outerChildren[k] = v end return React.createElement("Frame", { Size = size, Position = position, BackgroundTransparency = 1, ref = rootRef, }, { Drag = React.createElement("UIDragDetector"), OuterBorder = React.createElement("Frame", { Size = UDim2.new(1, 16, 1, 16), Position = UDim2.new(0.5, 0, 0.5, 0), AnchorPoint = Vector2.new(0.5, 0.5), BackgroundColor3 = Color3.fromRGB(200, 200, 200), BackgroundTransparency = 0.85, }, outerChildren) }) end --// mounting/root local screenGui = Instance.new("ScreenGui", CoreGui) --// can be switched to gethui() but dou to roblox new updates that protects internal services it's useless / saying this so i won't get complaints about "not using gethui or cloneref? so detected" screenGui.Name = "ReactUI : by sillzchibi" screenGui.ResetOnSpawn = false local root = RbxReact.createRoot(screenGui) local app = React.createElement(CreateReactUI) root:render(app)