Quick answer: The most common causes are a missing EventSystem in the scene, a missing GraphicRaycaster component on the Canvas, an invisible UI element blocking the button, or a CanvasGroup with interactable set to false.
Here is how to fix Unity UI button not responding clicks. Your Unity UI button looks perfect in the scene view, the onClick event is wired up, and your handler code is correct — but clicking the button at runtime does absolutely nothing. No log output, no state change, no response at all. This is one of the most common Unity UI issues, and it almost always comes down to a broken link somewhere in the UI event pipeline rather than a problem with your button code itself.
The Symptom
You have a Button component on a GameObject inside a Canvas. You have assigned an onClick listener either through the Inspector or via code using button.onClick.AddListener(). When you enter Play mode and click the button, nothing happens. The click callback never fires. There are no errors or warnings in the Console.
In some cases the button worked previously and stopped working after you reorganized your UI hierarchy, added a popup panel, or changed the Canvas render mode. In other cases it never worked from the start. The button may highlight on hover (indicating partial event detection) but still refuse to register clicks, or it may show no visual response at all.
What Causes This
Unity UI buttons depend on a multi-step event pipeline. A click has to travel from the input system through the EventSystem, get raycasted by the GraphicRaycaster on the Canvas, hit the button's RectTransform, pass through any CanvasGroup filters, and finally reach the Button component. If any single link in that chain is broken, the click silently fails with no error message.
- Missing EventSystem — Unity needs exactly one active EventSystem GameObject in the scene to process any UI input. If you deleted it accidentally, or if you loaded a scene additively that does not have one, no UI element in that scene will respond to input. Unity creates an EventSystem automatically when you add your first Canvas via the menu, but manual scene construction can skip this step.
- Missing GraphicRaycaster — Each Canvas needs a
GraphicRaycastercomponent to detect which UI elements the pointer is over. Without it, the EventSystem cannot determine what was clicked. This component is added automatically to new Canvases, but it can be accidentally removed or disabled. - Blocking UI element on top — An invisible or transparent Image, RawImage, or Panel sitting above your button in the hierarchy will intercept the raycast before it reaches the button. This is the most insidious cause because the blocking element is often completely invisible — a fullscreen Image with alpha set to zero still blocks raycasts by default.
- CanvasGroup settings — A
CanvasGroupon any parent of the button can disable interaction for all children. Ifinteractableis set tofalseorblocksRaycastsis set tofalse, buttons under that group will not respond. This commonly happens with fade-in/fade-out UI panels where the CanvasGroup alpha is animated but the interactable flag is not restored. - Canvas render mode issues — If your Canvas is set to Screen Space - Camera but no Render Camera is assigned, the GraphicRaycaster will not function. Similarly, World Space canvases require a camera reference and a
PhysicsRaycasteror properly configuredGraphicRaycaster. - Button interactable is false — The Button component itself has an
interactablecheckbox. If this is unchecked, the button will appear greyed out and will not fire onClick events.
The Fix
Step 1: Verify the EventSystem and GraphicRaycaster exist. Open your scene hierarchy and search for "EventSystem". If it does not exist, create one via GameObject > UI > Event System. Then select your Canvas and confirm it has a GraphicRaycaster component in the Inspector. You can also verify this at runtime with a diagnostic script:
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class UIClickDebugger : MonoBehaviour
{
void Start()
{
// Check for EventSystem
if (EventSystem.current == null)
{
Debug.LogError("No EventSystem found in scene! UI input will not work.");
Debug.LogError("Add one via GameObject > UI > Event System.");
return;
}
// Check all Canvases for GraphicRaycaster
Canvas[] canvases = FindObjectsByType<Canvas>(FindObjectsSortMode.None);
foreach (Canvas canvas in canvases)
{
GraphicRaycaster raycaster = canvas.GetComponent<GraphicRaycaster>();
if (raycaster == null)
{
Debug.LogWarning($"Canvas '{canvas.name}' is missing a GraphicRaycaster component.");
}
else if (!raycaster.enabled)
{
Debug.LogWarning($"Canvas '{canvas.name}' has a disabled GraphicRaycaster.");
}
// Check Screen Space - Camera without a camera assigned
if (canvas.renderMode == RenderMode.ScreenSpaceCamera
&& canvas.worldCamera == null)
{
Debug.LogError($"Canvas '{canvas.name}' is set to Screen Space - Camera but has no Render Camera assigned.");
}
}
Debug.Log("UI pipeline check complete.");
}
}
Step 2: Find and fix blocking UI elements. The most common culprit is a transparent Image or Panel that sits above your button in the hierarchy and intercepts raycasts. Every UI element with Raycast Target enabled will block clicks to elements behind it. The fix is to disable Raycast Target on any non-interactive UI element like background images, decorative icons, or text labels:
using UnityEngine;
using UnityEngine.UI;
public class RaycastTargetAuditor : MonoBehaviour
{
/// Call this from a debug menu or Editor script to find
/// all Graphic components with Raycast Target enabled.
[ContextMenu("Audit Raycast Targets")]
public void AuditRaycastTargets()
{
Graphic[] allGraphics = FindObjectsByType<Graphic>(FindObjectsSortMode.None);
int count = 0;
foreach (Graphic graphic in allGraphics)
{
if (graphic.raycastTarget)
{
// Check if this element is interactive
Button btn = graphic.GetComponent<Button>();
Toggle toggle = graphic.GetComponent<Toggle>();
Slider slider = graphic.GetComponent<Slider>();
InputField input = graphic.GetComponent<InputField>();
ScrollRect scroll = graphic.GetComponent<ScrollRect>();
bool isInteractive = btn != null || toggle != null
|| slider != null || input != null || scroll != null;
if (!isInteractive)
{
Debug.LogWarning(
$"Non-interactive element '{graphic.name}' has Raycast Target ON. "
+ "This may block clicks to elements behind it.",
graphic.gameObject
);
count++;
}
}
}
Debug.Log($"Audit complete. Found {count} non-interactive elements with Raycast Target enabled.");
}
/// Disable Raycast Target on all non-interactive Graphics
/// under a specific parent. Useful for cleaning up panels.
public static void DisableNonInteractiveRaycasts(Transform parent)
{
Graphic[] graphics = parent.GetComponentsInChildren<Graphic>();
foreach (Graphic graphic in graphics)
{
if (graphic.GetComponent<Selectable>() == null)
{
graphic.raycastTarget = false;
}
}
}
}
Step 3: Inspect CanvasGroup and interactable settings. If your button is inside a panel that fades in and out, the CanvasGroup controlling the fade may be disabling interaction. When you set a CanvasGroup's alpha to zero and then back to one, you also need to restore interactable and blocksRaycasts. Here is a proper fade panel implementation:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(CanvasGroup))]
public class UIPanel : MonoBehaviour
{
private CanvasGroup _canvasGroup;
void Awake()
{
_canvasGroup = GetComponent<CanvasGroup>();
}
public void Show(float duration = 0.3f)
{
StopAllCoroutines();
StartCoroutine(FadeIn(duration));
}
public void Hide(float duration = 0.3f)
{
StopAllCoroutines();
StartCoroutine(FadeOut(duration));
}
private IEnumerator FadeIn(float duration)
{
gameObject.SetActive(true);
float elapsed = 0f;
float startAlpha = _canvasGroup.alpha;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
_canvasGroup.alpha = Mathf.Lerp(startAlpha, 1f, elapsed / duration);
yield return null;
}
_canvasGroup.alpha = 1f;
// Restore interaction after fade completes
_canvasGroup.interactable = true;
_canvasGroup.blocksRaycasts = true;
}
private IEnumerator FadeOut(float duration)
{
// Disable interaction immediately so buttons
// cannot be clicked during the fade
_canvasGroup.interactable = false;
float elapsed = 0f;
float startAlpha = _canvasGroup.alpha;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
_canvasGroup.alpha = Mathf.Lerp(startAlpha, 0f, elapsed / duration);
yield return null;
}
_canvasGroup.alpha = 0f;
_canvasGroup.blocksRaycasts = false;
gameObject.SetActive(false);
}
/// Debug helper: log the full CanvasGroup state
[ContextMenu("Log CanvasGroup State")]
public void LogState()
{
Debug.Log(
$"Panel '{name}' - Alpha: {_canvasGroup.alpha}, "
+ $"Interactable: {_canvasGroup.interactable}, "
+ $"BlocksRaycasts: {_canvasGroup.blocksRaycasts}"
);
}
}
If your button still does not respond after these three steps, check one more thing: make sure the Button component's own interactable property is set to true in the Inspector. A greyed-out button appearance is the visual indicator that this is the issue. You can also verify at runtime:
using UnityEngine;
using UnityEngine.UI;
public class ButtonStateChecker : MonoBehaviour
{
[SerializeField] private Button _button;
void Start()
{
if (_button == null)
_button = GetComponent<Button>();
Debug.Log($"Button '{_button.name}' interactable: {_button.interactable}");
// Check all parent CanvasGroups
CanvasGroup[] groups = GetComponentsInParent<CanvasGroup>();
foreach (CanvasGroup group in groups)
{
if (!group.interactable)
Debug.LogWarning($"Parent CanvasGroup '{group.name}' has interactable=false");
if (!group.blocksRaycasts)
Debug.LogWarning($"Parent CanvasGroup '{group.name}' has blocksRaycasts=false");
}
// Wire up a test listener
_button.onClick.AddListener(() =>
{
Debug.Log("Button clicked successfully!");
});
}
}
Related Issues
See also: Fix: Unity AudioSource Not Playing Sound.
See also: Fix: Unity NavMeshAgent Not Moving to Destination.
Always check for an EventSystem and rogue Raycast Targets first.