diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/pom.xml b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/pom.xml
index 94e9c9ace3e2fc77a580668c1781514a55f3f4c8..e1a0ce38c919d63435efd66ea7274896e9889dae 100644
--- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/pom.xml
+++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/pom.xml
@@ -31,7 +31,7 @@
   <artifactId>xwiki-platform-livedata-api</artifactId>
   <name>XWiki Platform - Live Data - API</name>
   <properties>
-    <xwiki.jacoco.instructionRatio>0.69</xwiki.jacoco.instructionRatio>
+    <xwiki.jacoco.instructionRatio>0.70</xwiki.jacoco.instructionRatio>
     <checkstyle.suppressions.location>${basedir}/src/checkstyle/checkstyle-suppressions.xml</checkstyle.suppressions.location>
     <!-- Name to display by the Extension Manager -->
     <xwiki.extension.name>Live Data API</xwiki.extension.name>
diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/StringLiveDataConfigurationResolver.java b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/StringLiveDataConfigurationResolver.java
index 33d39a45776e3391ca146d7bd95e6ba377849af5..f50c8a099f4cfcd16cd4da7921e24ecd6f1cfd61 100644
--- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/StringLiveDataConfigurationResolver.java
+++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/StringLiveDataConfigurationResolver.java
@@ -48,6 +48,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
 
 /**
  * Resolves the live data configuration from a JSON string input.
@@ -89,6 +90,10 @@ public class StringLiveDataConfigurationResolver implements LiveDataConfiguratio
 
     private static final String ACTIONS = "actions";
 
+    private static final String CSS_CLASS = "cssClass";
+
+    private static final String EXTRA_ICON_CLASSES = "extraIconClasses";
+
     @Inject
     private Logger logger;
 
@@ -253,6 +258,33 @@ private void normalizeIcon(ObjectNode descriptor, ObjectMapper objectMapper)
         } else if (!icon.isObject()) {
             descriptor.remove(ICON);
         }
+        normalizeIconClasses(descriptor);
+    }
+
+    /**
+     * Adds the {@link #EXTRA_ICON_CLASSES} to the icon's CSS classes. It is done by looking for an
+     * {@link #EXTRA_ICON_CLASSES} field on the descriptor. If the {@link #EXTRA_ICON_CLASSES} field is not found,
+     * nothing happen. If it is found, it is concatenated at the end of the {@link #CSS_CLASS} field of the
+     * {@link #ICON} object. If the {@link #CSS_CLASS} is not present, it is initialized with the value of
+     * {@link #EXTRA_ICON_CLASSES}. The {@link #EXTRA_ICON_CLASSES} field is removed for the descriptor in all cases.
+     *
+     * @param descriptor the descriptor to normalize
+     */
+    private static void normalizeIconClasses(ObjectNode descriptor)
+    {
+        JsonNode icon = descriptor.path(ICON);
+        if (icon.isObject()) {
+            JsonNode extraClasses = descriptor.path(EXTRA_ICON_CLASSES);
+            if (extraClasses.isTextual()) {
+                String cssClasses = extraClasses.textValue().trim();
+                if (icon.path(CSS_CLASS).isTextual()) {
+                    cssClasses = icon.path(CSS_CLASS).textValue().trim() + " " + cssClasses;
+                }
+                ((ObjectNode) icon).set(CSS_CLASS, new TextNode(cssClasses));
+            }
+            // Does not need to be preserved once the icon is fully resolved.
+            descriptor.remove(EXTRA_ICON_CLASSES);
+        }
     }
 
     private void normalizeLayouts(ObjectNode metaConfig, ObjectMapper objectMapper)
diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/java/org/xwiki/livedata/internal/StringLiveDataConfigurationResolverTest.java b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/java/org/xwiki/livedata/internal/StringLiveDataConfigurationResolverTest.java
index 49c29f8f914d3e107ddd071d175ee85e7662edc8..8c115dfc9f089b759b16c8209a02c5e6e6588b6e 100644
--- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/java/org/xwiki/livedata/internal/StringLiveDataConfigurationResolverTest.java
+++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/java/org/xwiki/livedata/internal/StringLiveDataConfigurationResolverTest.java
@@ -33,6 +33,7 @@
 import org.xwiki.test.junit5.mockito.MockComponent;
 
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -73,15 +74,32 @@ void before() throws Exception
         tableIconMetaData.put(IconManager.META_DATA_ICON_SET_NAME, "Silk");
         tableIconMetaData.put(IconManager.META_DATA_URL, "/path/to/table.png");
 
+        Map<String, Object> crossIconMetadata = Map.of(
+            IconManager.META_DATA_ICON_SET_TYPE, "font",
+            IconManager.META_DATA_ICON_SET_NAME, "Font Awesome",
+            IconManager.META_DATA_CSS_CLASS, "fa fa-times"
+        );
+        
+        Map<String, Object> testIconMetadata = Map.of(
+            IconManager.META_DATA_ICON_SET_TYPE, "test",
+            IconManager.META_DATA_ICON_SET_NAME, "Test"
+        );
+
         when(this.iconManager.getMetaData("file")).thenReturn(fileIconMetaData);
         when(this.iconManager.getMetaData("table")).thenReturn(tableIconMetaData);
+        when(this.iconManager.getMetaData("cross")).thenReturn(crossIconMetadata);
+        when(this.iconManager.getMetaData("test")).thenReturn(testIconMetadata);
     }
 
     @ParameterizedTest
     @MethodSource("getTestData")
     void resolve(String message, String input, String output) throws Exception
     {
-        assertEquals(output, this.objectMapper.writeValueAsString(this.resolver.resolve(input)), message);
+        JsonNode expect = this.objectMapper.readValue(output, JsonNode.class);
+        JsonNode actual =
+            this.objectMapper.readValue(this.objectMapper.writeValueAsString(this.resolver.resolve(input)),
+                JsonNode.class);
+        assertEquals(expect, actual, message);
     }
 
     private static Stream<String[]> getTestData() throws Exception
diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/resources/StringLiveDataConfigurationResolver.test b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/resources/StringLiveDataConfigurationResolver.test
index 2ae42f3b12a4b118c06998d1cb22090daa0a1073..7cfe07400cb61c0ce4df04564751238999287521 100644
--- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/resources/StringLiveDataConfigurationResolver.test
+++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/resources/StringLiveDataConfigurationResolver.test
@@ -170,7 +170,11 @@
         "visible":false
       }
     ],
-    "actions":["edit"]
+    "actions":[
+      "edit",
+      {"id": "delete", "icon": "cross", "extraIconClasses": "text-danger"},
+      {"id": "test", "icon": "test", "extraIconClasses": "text-test"}
+    ]
   }
 }
 ---
@@ -193,7 +197,9 @@
       }
     ],
     "actions":[
-      {"id":"edit"}
+      {"id":"edit"},
+      {"id":"delete","icon":{"iconSetName":"Font Awesome","iconSetType":"font","cssClass":"fa fa-times text-danger"}},
+      {"id":"test","icon":{"iconSetName":"Test","iconSetType":"test","cssClass":"text-test"}}
     ]
   }
 }