diff --git a/modules/ui_patterns_library/src/StoriesSyntaxConverter.php b/modules/ui_patterns_library/src/StoriesSyntaxConverter.php
index 7c13c4392967b07f30c1e3b304cd289c31568381..4be791ebb1ab86cb4c35adcba57323842c96d3a4 100644
--- a/modules/ui_patterns_library/src/StoriesSyntaxConverter.php
+++ b/modules/ui_patterns_library/src/StoriesSyntaxConverter.php
@@ -11,88 +11,224 @@ namespace Drupal\ui_patterns_library;
  * Let's put them back.
  *
  * Before: ["type" => "component", "component" => "example:card"]
- * After:  ["#type" => "component", "#component" => "example:card"]
+ * After: ["#type" => "component", "#component" => "example:card"]
  */
 class StoriesSyntaxConverter {
 
   /**
    * An array with one (and only one) of those keys may be a render array.
    */
-  const RENDER_KEYS = ["theme", "type", "markup", "plain_text", "#theme", "#type", "#markup", "#plain_text"];
+  public const RENDER_KEYS = [
+    'markup',
+    'plain_text',
+    'theme',
+    'type',
+    '#markup',
+    '#plain_text',
+    '#theme',
+    '#type',
+  ];
+
+  public const KNOWN_PROPERTIES = [
+    'type' => [
+      'html_tag' => [
+        'attached',
+        'attributes',
+        'tag',
+        'type',
+        'value',
+      ],
+    ],
+    'theme' => [
+      'layout' => [
+        'attached',
+        'attributes',
+        'theme',
+        'settings',
+      ],
+    ],
+  ];
 
   /**
-   * Process stories slots.
+   * List of render properties which should have been children instead.
    */
-  public function convertSlots(array $slots): array {
-    foreach ($slots as $slot_id => $slot) {
-      if (!is_array($slot)) {
-        continue;
-      }
-      $slots[$slot_id] = $this->convertArray($slot);
-    }
-    return $slots;
-  }
+  public const PROPERTIES_INSTEAD_OF_CHILDREN = [
+    'type' => [
+      'component' => [
+        'slots',
+      ],
+    ],
+    'theme' => [
+      'status_messages' => [
+        'message_list',
+      ],
+      'table' => [
+        'header',
+        'rows',
+        'footer',
+        'empty',
+        'caption',
+      ],
+    ],
+  ];
 
   /**
-   * Convert an array.
+   * Process stories slots.
    */
-  protected function convertArray(array $array): array {
+  public function convertSlots(array $array): array {
     if ($this->isRenderArray($array)) {
       return $this->convertRenderArray($array);
     }
     foreach ($array as $index => $value) {
-      if (!is_array($value)) {
+      if (!\is_array($value)) {
         continue;
       }
-      $array[$index] = $this->convertArray($value);
+      $array[$index] = $this->convertSlots($value);
     }
     return $array;
   }
 
   /**
    * Convert a render array.
+   *
+   * @param array $renderable
+   *   The render array being processed.
+   *
+   * @return array
+   *   The processed render array.
    */
   protected function convertRenderArray(array $renderable): array {
-    foreach ($renderable as $property => $value) {
-      if (is_array($value)) {
-        $renderable[$property] = $this->convertArray($value);
+    // Weird detection.
+    if (isset($renderable['type'], self::PROPERTIES_INSTEAD_OF_CHILDREN['type'][$renderable['type']])) {
+      return $this->convertWeirdRenderArray($renderable, self::PROPERTIES_INSTEAD_OF_CHILDREN['type'][$renderable['type']]);
+    }
+    if (isset($renderable['theme'])) {
+      $baseThemeHook = \explode('__', $renderable['theme'])[0];
+      if (isset(self::PROPERTIES_INSTEAD_OF_CHILDREN['theme'][$baseThemeHook])) {
+        return $this->convertWeirdRenderArray($renderable, self::PROPERTIES_INSTEAD_OF_CHILDREN['theme'][$baseThemeHook]);
       }
     }
-    $in_html_tag = (isset($renderable["type"]) && $renderable["type"] === "html_tag");
-    $html_tag_allowed_render_keys = ["type", "attributes", "tag", "value", "attached"];
+
+    // Normal with special case detection.
+    if (isset($renderable['type'], self::KNOWN_PROPERTIES['type'][$renderable['type']])) {
+      return $this->convertNormalRenderArray($renderable, self::KNOWN_PROPERTIES['type'][$renderable['type']]);
+    }
+    if (isset($renderable['theme'])) {
+      $baseThemeHook = \explode('__', $renderable['theme'])[0];
+      if (isset(self::KNOWN_PROPERTIES['theme'][$baseThemeHook])) {
+        return $this->convertNormalRenderArray($renderable, self::KNOWN_PROPERTIES['theme'][$baseThemeHook]);
+      }
+    }
+
+    return $this->convertNormalRenderArray($renderable, []);
+  }
+
+  /**
+   * Add property prefix.
+   *
+   * @param array $renderable
+   *   The renderable array.
+   * @param mixed $property
+   *   The property.
+   *
+   * @return array
+   *   The array with prefixed property.
+   */
+  protected function convertProperty(array $renderable, mixed $property): array {
+    if (!\is_string($property)) {
+      return $renderable;
+    }
+    if (\str_starts_with($property, '#')) {
+      return $renderable;
+    }
+    $renderable['#' . $property] = $renderable[$property];
+    unset($renderable[$property]);
+    return $renderable;
+  }
+
+  /**
+   * To convert "normal" render array.
+   *
+   * A "normal" render arrays is an array:
+   * - where properties (key starts with a '#') are not renderables
+   * - children (key does not start with a '#') are only renderables.
+   *
+   * Examples:
+   * - html_tag which is forbidding renderables in #value
+   * - layout where every region is a child.
+   *
+   * @param array $renderable
+   *   The renderable array.
+   * @param array $knownProperties
+   *   The list of know properties.
+   *
+   * @return array
+   *   The converted array.
+   */
+  protected function convertNormalRenderArray(array $renderable, array $knownProperties): array {
     foreach ($renderable as $property => $value) {
-      if (!is_string($property)) {
-        continue;
+      if (empty($knownProperties) && \is_string($property)) {
+        // Default to add prefix to every entries.
+        $renderable = $this->convertProperty($renderable, $property);
       }
-      // html_tag is special.
-      if ($in_html_tag && !in_array($property, $html_tag_allowed_render_keys)) {
-        continue;
+      elseif (\in_array($property, $knownProperties, TRUE)) {
+        // We add # prefix only to known properties.
+        $renderable = $this->convertProperty($renderable, $property);
       }
-      if (str_starts_with($property, "#")) {
-        continue;
+      elseif (\is_array($value)) {
+        // Other keys may have children so let's drill.
+        $renderable[$property] = $this->convertSlots($value);
+      }
+    }
+    return $renderable;
+  }
+
+  /**
+   * The "weird" render arrays, where renderables are found only in properties.
+   *
+   * Examples: component with #slots, table with #rows...
+   *
+   * @param array $renderable
+   *   The renderable array.
+   * @param array $propertiesWithRenderables
+   *   The list of properties to look for.
+   *
+   * @return array
+   *   The updated renderable array.
+   */
+  protected function convertWeirdRenderArray(array $renderable, array $propertiesWithRenderables): array {
+    foreach ($renderable as $property => $value) {
+      if (\in_array($property, $propertiesWithRenderables, TRUE) && \is_array($value)) {
+        $renderable[$property] = $this->convertSlots($value);
       }
-      $renderable["#" . $property] = $value;
-      unset($renderable[$property]);
+      // There are no children, so we add a # everywhere.
+      $renderable = $this->convertProperty($renderable, $property);
     }
     return $renderable;
   }
 
   /**
    * Is the array a render array?
+   *
+   * @param array $array
+   *   The array being processed.
+   *
+   * @return bool
+   *   True if a render array.
    */
   protected function isRenderArray(array $array): bool {
-    if (array_is_list($array)) {
+    if (\array_is_list($array)) {
       return FALSE;
     }
     // An array needs one, and only one, of those properties to be a render
     // array.
-    $intersect = array_intersect(array_keys($array), self::RENDER_KEYS);
-    if (count($intersect) != 1) {
+    $intersect = \array_intersect(\array_keys($array), self::RENDER_KEYS);
+    if (\count($intersect) != 1) {
       return FALSE;
     }
     // This property has to be a string value.
-    $property = $intersect[array_key_first($intersect)];
-    if (!is_string($array[$property])) {
+    $property = $intersect[\array_key_first($intersect)];
+    if (!\is_string($array[$property])) {
       return FALSE;
     }
     return TRUE;
diff --git a/modules/ui_patterns_library/tests/src/Unit/StoriesSyntaxConversionTest.php b/modules/ui_patterns_library/tests/src/Unit/StoriesSyntaxConversionTest.php
index ffb21518e3d16cf8652b75da94338f09bc2887b5..aa6e28876d2745328456d25c28f9eb105b0f64f5 100644
--- a/modules/ui_patterns_library/tests/src/Unit/StoriesSyntaxConversionTest.php
+++ b/modules/ui_patterns_library/tests/src/Unit/StoriesSyntaxConversionTest.php
@@ -8,9 +8,10 @@ use Drupal\Tests\UnitTestCase;
 use Drupal\ui_patterns_library\StoriesSyntaxConverter;
 
 /**
- * @coversDefaultClass \Drupal\ui_patterns\Plugin\UiPatterns\PropType\LinksPropType
+ * @coversDefaultClass \Drupal\ui_patterns_library\StoriesSyntaxConverter
  *
  * @group ui_patterns
+ * @group ui_patterns_library
  */
 final class StoriesSyntaxConversionTest extends UnitTestCase {
 
@@ -22,7 +23,7 @@ final class StoriesSyntaxConversionTest extends UnitTestCase {
   public function testConvertSlot(array $value, array $expected): void {
     $converter = new StoriesSyntaxConverter();
     $converted = $converter->convertSlots($value);
-    self::assertEquals($converted, $expected);
+    self::assertEquals($expected, $converted);
   }
 
   /**
@@ -34,6 +35,7 @@ final class StoriesSyntaxConversionTest extends UnitTestCase {
       "Bootstrap accordion" => self::bootstrapAccordion(),
       "Bootstrap carousel" => self::bootstrapCarousel(),
       "Daisy Grid Row 4" => self::daisyGridRow4(),
+      "Props special words" => self::componentSpecialProps(),
     ];
     foreach ($data as $label => $test) {
       yield $label => [
@@ -296,4 +298,48 @@ final class StoriesSyntaxConversionTest extends UnitTestCase {
     ];
   }
 
+  /**
+   * Fake component with props with reserved words.
+   */
+  protected static function componentSpecialProps(): array {
+    $slots = [
+      'component' => [
+        'type' => 'component',
+        'component' => 'foo:bar',
+        'props' => [
+          'attributes' => [
+            'class' => ['bar'],
+            'type' => 'bar',
+          ],
+          'tag' => 'bar',
+          'type' => 'bar',
+          'value' => 'bar',
+          'attached' => 'bar',
+        ],
+      ],
+    ];
+    $expected = [
+      'component' => [
+        '#type' => 'component',
+        '#component' => 'foo:bar',
+        '#props' => [
+          'attributes' => [
+            'class' => [
+              0 => 'bar',
+            ],
+            'type' => 'bar',
+          ],
+          'tag' => 'bar',
+          'type' => 'bar',
+          'value' => 'bar',
+          'attached' => 'bar',
+        ],
+      ],
+    ];
+    return [
+      'value' => $slots,
+      'expected' => $expected,
+    ];
+  }
+
 }