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.

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.