Tutorial · Gameplay

How to use a ClickDetector in Roblox

A ClickDetector makes any part clickable: a button on a wall, a lever, a buzzer on a game show desk, a box that gives an item. Put one inside a part and the cursor changes when a player hovers it, and a server Script is told who clicked. This guide makes a clickable button, adds hover effects, runs many buttons from one script, and explains when a ProximityPrompt is the better choice.

Updated 5 November 20265 min readBeginnerLuauWritten by

In short

Insert a ClickDetector into a Part, and in a server Script connect clickDetector.MouseClick. It passes the Player who clicked, so you can reward or check them. MaxActivationDistance (32 studs by default) sets how close they must be, MouseHoverEnter and MouseHoverLeave fire when the cursor moves on and off, and RightMouseClick handles right clicks.

  • Handle MouseClick on the server, so the result is shared by everyone.
  • Add a cooldown and a distance check on the server: a cheater can fire clicks from anywhere, much faster than anyone can click.
  • For phones and controllers, a ProximityPrompt is usually easier to use than a tap on a small part.

Get the script

ClickDetector button script with a cooldown

A server Script that makes a part a clickable button: it lights up, says who pressed it, checks they are close and can only be pressed again after a cooldown.

Script in the Button part named ClickButton
-- Script inside the Button part, named ClickButton.
-- Lights up when clicked, then can be pressed again after a cooldown.
local button = script.Parent :: BasePart
local clickDetector = button:WaitForChild("ClickDetector") :: ClickDetector

local COOLDOWN_SECONDS = 2
local OFF_COLOR = Color3.fromRGB(200, 40, 40)
local ON_COLOR = Color3.fromRGB(40, 200, 80)

local pressedAt = -math.huge
button.Color = OFF_COLOR

clickDetector.MouseClick:Connect(function(player: Player)
	local now = os.clock()
	if now - pressedAt < COOLDOWN_SECONDS then
		return
	end
	-- cheats can fire clicks from any distance, so check it here
	local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
	if not (root and root:IsA("BasePart")) or (root.Position - button.Position).Magnitude > clickDetector.MaxActivationDistance + 5 then
		return
	end
	pressedAt = now
	print(player.Name, "pressed the button")

	button.Color = ON_COLOR
	task.delay(COOLDOWN_SECONDS, function()
		button.Color = OFF_COLOR
	end)
end)

RoCode builds it in the place you have open in Roblox Studio, on Windows or macOS with a desktop browser; on a phone the button sends the prompt and the link to your computer. Free to start, with free credits every day. More Roblox Studio scripts

Where to put it

Explorer: the ClickDetector and the Script inside the part
Workspace
  Button Part new
    ClickDetector ClickDetector new
    ClickButton Script new

Settings you can change

COOLDOWN_SECONDSIn the script
Seconds before the button can be pressed again. Default: 2.

What a cheater can and cannot doThe cooldown and the distance check run on the server, so clicking faster, or firing clicks from across the map with a cheat, does not press the button.

Type-checked with luau-lsp strict against Roblox's API definitions on 5 November 2026. Not playtested in Studio yet: press Play and watch the Output window before you publish.

Prompt for RoCode

Make the part named Button clickable with a ClickDetector: when a player clicks it, it turns green for 2 seconds, prints who pressed it and opens the gate model, with a 2 second server cooldown.

On this page
  1. Get the script
  2. How it works
  3. 1. Button script
  4. 2. Hover
  5. 3. Many buttons
  6. Click or prompt
  7. Common errors
  8. With RoCode
  9. Questions
  10. Sources

How a ClickDetector works

A ClickDetector works on the part it is inside (or on every part of a Model it is inside). When a player's cursor is over that part and within MaxActivationDistance of their character, the cursor changes to a hand, and a click fires MouseClick on the server with that player. On touch screens a tap on the part counts as a click.

ClickDetector events and properties
NameWhat it does
MouseClickLeft click (or tap). Passes the player.
RightMouseClickRight click. Passes the player.
MouseHoverEnter / MouseHoverLeaveThe cursor moved onto or off the part. Passes the player.
MaxActivationDistanceHow close, in studs, the player's character must be. 32 by default.
CursorIconA custom cursor image while hovering.

Step 1: A button that lights up

  1. Insert a Part in Workspace named Button, make it small and red, and tick Anchored.
  2. Hover over the part, click + and insert a ClickDetector.
  3. Insert a Script into the part named ClickButton and paste this:
Script in the Button part named ClickButton
-- Script inside the Button part, named ClickButton.
-- Lights up when clicked, then can be pressed again after a cooldown.
local button = script.Parent :: BasePart
local clickDetector = button:WaitForChild("ClickDetector") :: ClickDetector

local COOLDOWN_SECONDS = 2
local OFF_COLOR = Color3.fromRGB(200, 40, 40)
local ON_COLOR = Color3.fromRGB(40, 200, 80)

local pressedAt = -math.huge
button.Color = OFF_COLOR

clickDetector.MouseClick:Connect(function(player: Player)
	local now = os.clock()
	if now - pressedAt < COOLDOWN_SECONDS then
		return
	end
	-- cheats can fire clicks from any distance, so check it here
	local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
	if not (root and root:IsA("BasePart")) or (root.Position - button.Position).Magnitude > clickDetector.MaxActivationDistance + 5 then
		return
	end
	pressedAt = now
	print(player.Name, "pressed the button")

	button.Color = ON_COLOR
	task.delay(COOLDOWN_SECONDS, function()
		button.Color = OFF_COLOR
	end)
end)

The colour changes on the server, so every player sees the button light up. The distance check matters: Roblox's security guide warns that a cheater can fire a ClickDetector's events from any distance, ignoring MaxActivationDistance, so the server measures it again (with 5 studs to spare for movement). Put whatever the button does where the print is: open a door with the door guide's code, start a round, or give an item.

Step 2: Hover effects

A highlight when the cursor is over the part shows players it is clickable. Do this on the client, so only the player hovering sees it. Add a LocalScript in StarterPlayerScripts:

LocalScript in StarterPlayerScripts named ButtonHover
-- LocalScript in StarterPlayerScripts named ButtonHover.
-- Outlines the Button part while this player's cursor is over it.
local button = workspace:WaitForChild("Button") :: BasePart
local clickDetector = button:WaitForChild("ClickDetector") :: ClickDetector

local highlight = Instance.new("Highlight")
highlight.FillTransparency = 1
highlight.OutlineColor = Color3.new(1, 1, 1)
highlight.Enabled = false
highlight.Adornee = button
highlight.Parent = button

clickDetector.MouseHoverEnter:Connect(function()
	highlight.Enabled = true
end)

clickDetector.MouseHoverLeave:Connect(function()
	highlight.Enabled = false
end)

ClickDetector events fire on the client too, for the local player only, which is what makes this work. The Highlight is created by the LocalScript, so other players never see it.

Step 3: One script for many clickable parts

For a wall of buttons or a dozen item boxes, tag them instead of copying the script. Give each part the tag Clickable and a string attribute Message; this Script adds the ClickDetector itself.

Script in ServerScriptService named Clickables
-- Script in ServerScriptService named Clickables.
local CollectionService = game:GetService("CollectionService")

local TAG = "Clickable"
local COOLDOWN_SECONDS = 1

local function setUp(part: Instance)
	if not part:IsA("BasePart") or part:FindFirstChildOfClass("ClickDetector") then
		return
	end
	local detector = Instance.new("ClickDetector")
	detector.MaxActivationDistance = 20
	detector.Parent = part

	local lastClick: { [Player]: number } = {}
	detector.MouseClick:Connect(function(player: Player)
		local now = os.clock()
		if now - (lastClick[player] or 0) < COOLDOWN_SECONDS then
			return
		end
		local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
		if not (root and root:IsA("BasePart")) or (root.Position - part.Position).Magnitude > detector.MaxActivationDistance + 5 then
			return -- too far away to have clicked honestly
		end
		lastClick[player] = now
		local message = part:GetAttribute("Message")
		print(player.Name, "clicked", part.Name, if typeof(message) == "string" then message else "")
	end)
end

for _, part in CollectionService:GetTagged(TAG) do
	setUp(part)
end
CollectionService:GetInstanceAddedSignal(TAG):Connect(setUp)

Each part gets its own cooldown per player here, so one player clicking does not block another. The CollectionService guide covers tags and cleaning up when parts are removed.

ClickDetector or ProximityPrompt?

Which one to use
ClickDetectorProximityPrompt
How players use itClick or tap the part itselfPress E, a gamepad button or tap the prompt
Shows a hintOnly a cursor change on hoverA labelled bubble with the key
GamepadsAwkward: needs a virtual cursorBuilt in
Hold to useNoYes, with HoldDuration
Good forDesktop-first games, small buttons in reach of the mouse, many items close togetherDoors, NPCs, chests, anything in most games

Roblox's security guide says both must be secured as carefully as RemoteEvents, because a cheater can trigger them from any distance, so keep cooldowns and distance checks on the server either way. The ProximityPrompt guide builds the prompt version.

Common errors and fixes

Open the Output window (Window menu, then Output) before you press Play.

The cursor does not change over the part

You are further than MaxActivationDistance (32 studs) from it, the ClickDetector is not inside the part (or its Model), or another part in front of it is blocking the cursor.

Clicking does nothing

Check the Script is a Script inside the part or in ServerScriptService, that it found the right ClickDetector, and add print(player.Name) at the top of the handler.

It works for me but not on my phone

Taps count as clicks, but small parts are hard to hit with a finger. Make the part bigger or use a ProximityPrompt.

Build it with RoCode

If you would rather not set this up by hand, RoCode can build it in the place you have open. RoCode is an independent AI agent for Roblox Studio, not made by Roblox: you describe the change in a web chat, and its Studio plugin creates the scripts and instances in your Explorer, so there is no code to paste.

You type

Make the three red buttons on the wall clickable: each one, when clicked, lights up green for 2 seconds and opens its matching gate, with a server cooldown per button and an outline when hovered.

RoCode does
  • Can find the buttons and gates you mean with Search Game before it writes anything
  • Adds the ClickDetectors with Create Instance and writes the scripts with Create Script
  • Is told to compile-check each script it writes with Check Script
  • Sets an undo point before and after each batch of changes, so Ctrl+Z steps back one batch at a time

Click each button yourself in a playtest, and try it in Studio's Device emulator at phone size. How RoCode connects to Studio.

Questions

How do I make a part clickable in Roblox?

Insert a ClickDetector into the part and connect its MouseClick event in a server Script. The event passes the player who clicked.

How do I know who clicked a ClickDetector?

MouseClick gives you the Player as its first value: clickDetector.MouseClick:Connect(function(player) ... end).

Does ClickDetector work on mobile?

Yes, a tap on the part counts as a click. For small parts, a ProximityPrompt is easier to use on touch screens and gamepads.

How do I change how far away players can click?

Set the ClickDetector's MaxActivationDistance. The default is 32 studs.

Sources and further reading

How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 5 November 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.

Try RoCode on your own place.

Free every day, no card required. Install the plugin, describe the feature, and review what lands in Studio.