diff --git a/src/Tags/ComponentProxy.php b/src/Tags/ComponentProxy.php
index 4152a4b735b..5a18593cc87 100644
--- a/src/Tags/ComponentProxy.php
+++ b/src/Tags/ComponentProxy.php
@@ -2,6 +2,7 @@
namespace Statamic\Tags;
+use Illuminate\Support\Str;
use Illuminate\View\AnonymousComponent;
use Illuminate\View\Compilers\BladeCompiler;
use Illuminate\View\Compilers\ComponentTagCompiler;
@@ -51,12 +52,22 @@ public function index()
}
if ($constructor = (new ReflectionClass($className))->getConstructor()) {
- $constructorParameters = collect($constructor->getParameters())->map->getName()->all();
- $attributes = $attributes->except($constructorParameters);
- $constructorParameters = collect($scopeData)->only($constructorParameters)->all();
+ $parameterNames = collect($constructor->getParameters())->map->getName()->all();
+
+ // Kebab-cased attributes (e.g. :some-prop) should bind to camelCase
+ // constructor parameters ($someProp), mirroring Laravel's native behavior.
+ $attributes = $attributes->filter(fn ($value, $key) => ! in_array(Str::camel($key), $parameterNames));
+
+ $constructorParameters = collect($scopeData)
+ ->mapWithKeys(fn ($value, $key) => [Str::camel($key) => $value])
+ ->only($parameterNames)
+ ->all();
}
if ($isAnonymous) {
+ // Camel-case data keys so kebab-cased attributes resolve to the
+ // component's @props (e.g. :some-prop -> $someProp), as Laravel does.
+ $data = collect($data)->mapWithKeys(fn ($value, $key) => [Str::camel($key) => $value])->all();
$constructorParameters = array_merge($constructorParameters, $data, ['view' => $anonymousViewName, 'data' => $data]);
}
diff --git a/tests/Antlers/Components/BladeComponentsTest.php b/tests/Antlers/Components/BladeComponentsTest.php
index 1cf233752f0..d376dcb40d8 100644
--- a/tests/Antlers/Components/BladeComponentsTest.php
+++ b/tests/Antlers/Components/BladeComponentsTest.php
@@ -5,6 +5,7 @@
use Illuminate\Support\Facades\Blade;
use Statamic\View\Antlers\Language\Utilities\StringUtilities;
use Tests\Antlers\Fixtures\Components\Card;
+use Tests\Antlers\Fixtures\Components\KebabProp;
use Tests\Antlers\ParserTestCase;
class BladeComponentsTest extends ParserTestCase
@@ -126,6 +127,58 @@ public function test_components_blade_compatibility()
);
}
+ public function test_kebab_cased_attributes_bind_props_on_anonymous_blade_components()
+ {
+ $template = <<<'EOT'
+{{ foo = 'Test' }}