diff --git a/README.md b/README.md
index d5afb11..4af1f21 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,68 @@ final class TodoList
+### LiveListenerMethodsShouldBePublicRule
+
+Enforces that all methods annotated with `#[LiveListener]` in LiveComponents must be declared as public.
+LiveListener methods need to be publicly accessible to be invoked when listening to events from the frontend.
+
+```yaml
+rules:
+ - Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveListenerMethodsShouldBePublicRule
+```
+
+```php
+// src/Twig/Components/Notification.php
+namespace App\Twig\Components;
+
+use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
+use Symfony\UX\LiveComponent\Attribute\LiveListener;
+
+#[AsLiveComponent]
+final class Notification
+{
+ #[LiveListener('notification:received')]
+ private function onNotificationReceived(): void
+ {
+ }
+
+ #[LiveListener('notification:dismissed')]
+ protected function onNotificationDismissed(): void
+ {
+ }
+}
+```
+
+:x:
+
+
+
+```php
+// src/Twig/Components/Notification.php
+namespace App\Twig\Components;
+
+use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
+use Symfony\UX\LiveComponent\Attribute\LiveListener;
+
+#[AsLiveComponent]
+final class Notification
+{
+ #[LiveListener('notification:received')]
+ public function onNotificationReceived(): void
+ {
+ }
+
+ #[LiveListener('notification:dismissed')]
+ public function onNotificationDismissed(): void
+ {
+ }
+}
+```
+
+:+1:
+
+
+
## TwigComponent Rules
> [!NOTE]
diff --git a/src/Rules/LiveComponent/LiveListenerMethodsShouldBePublicRule.php b/src/Rules/LiveComponent/LiveListenerMethodsShouldBePublicRule.php
new file mode 100644
index 0000000..f545c7b
--- /dev/null
+++ b/src/Rules/LiveComponent/LiveListenerMethodsShouldBePublicRule.php
@@ -0,0 +1,53 @@
+
+ */
+final class LiveListenerMethodsShouldBePublicRule implements Rule
+{
+ public function getNodeType(): string
+ {
+ return Class_::class;
+ }
+
+ public function processNode(Node $node, Scope $scope): array
+ {
+ if (! AttributeFinder::findAnyAttribute($node, [AsLiveComponent::class])) {
+ return [];
+ }
+
+ $errors = [];
+
+ foreach ($node->getMethods() as $method) {
+ if (! AttributeFinder::findAnyAttribute($method, [LiveListener::class])) {
+ continue;
+ }
+
+ if (! $method->isPublic()) {
+ $errors[] = RuleErrorBuilder::message(sprintf(
+ 'LiveListener method "%s()" should be public.',
+ $method->name->toString()
+ ))
+ ->identifier('symfonyUX.liveComponent.liveListenerMethodShouldBePublic')
+ ->line($method->getLine())
+ ->tip('Change the method visibility to public.')
+ ->build();
+ }
+ }
+
+ return $errors;
+ }
+}
diff --git a/tests/Rules/LiveComponent/LiveListenerMethodsShouldBePublicRule/Fixture/NoLiveListener.php b/tests/Rules/LiveComponent/LiveListenerMethodsShouldBePublicRule/Fixture/NoLiveListener.php
new file mode 100644
index 0000000..e49dd36
--- /dev/null
+++ b/tests/Rules/LiveComponent/LiveListenerMethodsShouldBePublicRule/Fixture/NoLiveListener.php
@@ -0,0 +1,19 @@
+
+ */
+final class LiveListenerMethodsShouldBePublicRuleTest extends RuleTestCase
+{
+ public function testViolations(): void
+ {
+ $this->analyse(
+ [__DIR__ . '/Fixture/PrivateLiveListener.php'],
+ [
+ [
+ 'LiveListener method "onAnotherEvent()" should be public.',
+ 13,
+ 'Change the method visibility to public.',
+ ],
+ [
+ 'LiveListener method "onSomeEvent()" should be public.',
+ 18,
+ 'Change the method visibility to public.',
+ ],
+ ]
+ );
+ }
+
+ public function testNoViolations(): void
+ {
+ $this->analyse(
+ [__DIR__ . '/Fixture/NotAComponent.php'],
+ []
+ );
+
+ $this->analyse(
+ [__DIR__ . '/Fixture/PublicLiveListener.php'],
+ []
+ );
+
+ $this->analyse(
+ [__DIR__ . '/Fixture/NoLiveListener.php'],
+ []
+ );
+ }
+
+ public static function getAdditionalConfigFiles(): array
+ {
+ return [__DIR__ . '/config/configured_rule.neon'];
+ }
+
+ protected function getRule(): Rule
+ {
+ return self::getContainer()->getByType(LiveListenerMethodsShouldBePublicRule::class);
+ }
+}
diff --git a/tests/Rules/LiveComponent/LiveListenerMethodsShouldBePublicRule/config/configured_rule.neon b/tests/Rules/LiveComponent/LiveListenerMethodsShouldBePublicRule/config/configured_rule.neon
new file mode 100644
index 0000000..ffce0ec
--- /dev/null
+++ b/tests/Rules/LiveComponent/LiveListenerMethodsShouldBePublicRule/config/configured_rule.neon
@@ -0,0 +1,2 @@
+rules:
+ - Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveListenerMethodsShouldBePublicRule