Nested artboard types: node, leaf, layout

~~Adding as a draft to start testing.~~

- [x] Add node, leaf, and layout types.
- [x] Leaf supports fit and alignment.
- [x] Leaf alignment is floating point instead of enum allowing for animation later.
- [x] Layout allows external hosting artboards to take the nested layout node and host it in another hierarchy.
- [x] Measure and control size for NestedArtboard from native.
- [x] FFI changes for external layout node
- [x] WASM changes for external layout node.
- [x] Move layouts to RiveNative.

<img width="663" alt="CleanShot 2024-07-21 at 14 48 41@2x" src="https://github.com/user-attachments/assets/b2c70d55-5f09-421a-bde3-f49939c6effc">

~~For a follow up PR:
I think it's time to move some of rive_common into rive_native. I think the layout stuff would be a good start. @philter take a look at how the rive_binding.cpp is shared in rive_native for both FFI and WASM. I think it would simplify our layout bindings to use this model too.~~
Nevermind, we need it for this PR or ```LayoutNode.fromExternal``` doesn't work.

Diffs=
1a5f273bb Nested artboard types: node, leaf, layout (#7639)

Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
diff --git a/.rive_head b/.rive_head
index 9664e69..fadf9ed 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-50bc398c464061bb2ab5ac51e42901053ae63f1f
+1a5f273bb6534a395d1c3cdcc11a1ba3cd80a96c
diff --git a/dev/defs/nested_artboard.json b/dev/defs/nested_artboard.json
index 112415c..8a1ea48 100644
--- a/dev/defs/nested_artboard.json
+++ b/dev/defs/nested_artboard.json
@@ -17,29 +17,13 @@
       },
       "description": "Identifier used to track the Artboard nested."
     },
-    "fit": {
-      "type": "uint",
-      "key": {
-        "int": 538,
-        "string": "fit"
-      },
-      "description": "Fit type for the nested artboard's runtime artboard."
-    },
-    "alignment": {
-      "type": "uint",
-      "key": {
-        "int": 539,
-        "string": "alignment"
-      },
-      "description": "Alignment type for the nested artboard's runtime artboard."
-    },
     "dataBindPathIds": {
       "type": "List<Id>",
       "typeRuntime": "Bytes",
       "encoded": true,
       "initialValue": "[]",
       "key": {
-        "int": 580,
+        "int": 582,
         "string": "databindpathids"
       },
       "description": "Path to the selected property."
diff --git a/dev/defs/nested_artboard_layout.json b/dev/defs/nested_artboard_layout.json
new file mode 100644
index 0000000..b0579be
--- /dev/null
+++ b/dev/defs/nested_artboard_layout.json
@@ -0,0 +1,8 @@
+{
+    "name": "NestedArtboardLayout",
+    "key": {
+        "int": 452,
+        "string": "nestedartboardlayout"
+    },
+    "extends": "nested_artboard.json"
+}
\ No newline at end of file
diff --git a/dev/defs/nested_artboard_leaf.json b/dev/defs/nested_artboard_leaf.json
new file mode 100644
index 0000000..cddf158
--- /dev/null
+++ b/dev/defs/nested_artboard_leaf.json
@@ -0,0 +1,34 @@
+{
+    "name": "NestedArtboardLeaf",
+    "key": {
+        "int": 451,
+        "string": "nested_artboard_leaf"
+    },
+    "extends": "nested_artboard.json",
+    "properties": {
+        "fit": {
+            "type": "uint",
+            "key": {
+                "int": 538,
+                "string": "fit"
+            },
+            "description": "Fit type for the nested artboard's runtime artboard."
+        },
+        "alignmentX": {
+            "type": "double",
+            "key": {
+                "int": 644,
+                "string": "alignmentx"
+            },
+            "description": "Alignment value on X."
+        },
+        "alignmentY": {
+            "type": "double",
+            "key": {
+                "int": 645,
+                "string": "alignmenty"
+            },
+            "description": "Alignment value on Y."
+        }
+    }
+}
\ No newline at end of file
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp
index 4b7cc3d..2e78c7e 100644
--- a/include/rive/artboard.hpp
+++ b/include/rive/artboard.hpp
@@ -77,6 +77,10 @@
     std::unordered_set<LayoutComponent*> m_dirtyLayout;
     float m_originalWidth = 0;
     float m_originalHeight = 0;
+    bool m_updatesOwnLayout = true;
+    Artboard* parentArtboard() const;
+    NestedArtboard* m_host = nullptr;
+    bool sharesLayoutWithHost() const;
 
 #ifdef EXTERNAL_RIVE_AUDIO_ENGINE
     rcp<AudioEngine> m_audioEngine;
@@ -85,8 +89,14 @@
     void sortDependencies();
     void sortDrawOrder();
     void updateDataBinds();
-    void performUpdate(ComponentDirt value) override;
+    void updateRenderPath() override;
+    void update(ComponentDirt value) override;
 
+public:
+    void host(NestedArtboard* nestedArtboard);
+    NestedArtboard* host() const;
+
+private:
 #ifdef TESTING
 public:
     Artboard(Factory* factory) : m_Factory(factory) {}
@@ -96,7 +106,7 @@
     void addStateMachine(StateMachine* object);
 
 public:
-    Artboard() {}
+    Artboard();
     ~Artboard() override;
     StatusCode initialize();
 
@@ -124,21 +134,10 @@
     // Artboard is a special type of LayoutComponent
     void updateWorldTransform() override {}
 
-    void markLayoutDirty(LayoutComponent* layoutComponent)
-    {
-        m_dirtyLayout.insert(layoutComponent);
-    }
+    void markLayoutDirty(LayoutComponent* layoutComponent);
 
-#ifdef WITH_RIVE_LAYOUT
-    AABB layoutBounds() override
-    {
-        if (!hasLayoutMeasurements())
-        {
-            return AABB(x(), y(), x() + width(), y() + height());
-        }
-        return Super::layoutBounds();
-    }
-#endif
+    void* takeLayoutNode();
+    bool syncStyleChanges();
 
     bool advance(double elapsedSeconds, bool nested = true);
     bool advanceInternal(double elapsedSeconds, bool isRoot, bool nested = true);
@@ -171,7 +170,10 @@
     float originalHeight() const { return m_originalHeight; }
     float layoutWidth() const;
     float layoutHeight() const;
+    float layoutX() const;
+    float layoutY() const;
     AABB bounds() const;
+    Vec2D origin() const;
 
     // Can we hide these from the public? (they use playable)
     bool isTranslucent() const;
@@ -366,9 +368,11 @@
     float m_volume = 1.0f;
 #ifdef WITH_RIVE_TOOLS
     ArtboardCallback m_layoutChangedCallback = nullptr;
+    ArtboardCallback m_layoutDirtyCallback = nullptr;
 
 public:
     void onLayoutChanged(ArtboardCallback callback) { m_layoutChangedCallback = callback; }
+    void onLayoutDirty(ArtboardCallback callback) { m_layoutDirtyCallback = callback; }
 #endif
 };
 
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp
index abeae9a..7abe2d5 100644
--- a/include/rive/generated/core_registry.hpp
+++ b/include/rive/generated/core_registry.hpp
@@ -117,6 +117,8 @@
 #include "rive/layout_component.hpp"
 #include "rive/nested_animation.hpp"
 #include "rive/nested_artboard.hpp"
+#include "rive/nested_artboard_layout.hpp"
+#include "rive/nested_artboard_leaf.hpp"
 #include "rive/node.hpp"
 #include "rive/open_url_event.hpp"
 #include "rive/shapes/clipping_shape.hpp"
@@ -258,6 +260,8 @@
                 return new NestedArtboard();
             case SoloBase::typeKey:
                 return new Solo();
+            case NestedArtboardLayoutBase::typeKey:
+                return new NestedArtboardLayout();
             case LayoutComponentStyleBase::typeKey:
                 return new LayoutComponentStyle();
             case ListenerFireEventBase::typeKey:
@@ -428,6 +432,8 @@
                 return new BindablePropertyEnum();
             case BindablePropertyColorBase::typeKey:
                 return new BindablePropertyColor();
+            case NestedArtboardLeafBase::typeKey:
+                return new NestedArtboardLeaf();
             case WeightBase::typeKey:
                 return new Weight();
             case BoneBase::typeKey:
@@ -639,12 +645,6 @@
             case NestedArtboardBase::artboardIdPropertyKey:
                 object->as<NestedArtboardBase>()->artboardId(value);
                 break;
-            case NestedArtboardBase::fitPropertyKey:
-                object->as<NestedArtboardBase>()->fit(value);
-                break;
-            case NestedArtboardBase::alignmentPropertyKey:
-                object->as<NestedArtboardBase>()->alignment(value);
-                break;
             case NestedAnimationBase::animationIdPropertyKey:
                 object->as<NestedAnimationBase>()->animationId(value);
                 break;
@@ -951,6 +951,9 @@
             case BindablePropertyEnumBase::propertyValuePropertyKey:
                 object->as<BindablePropertyEnumBase>()->propertyValue(value);
                 break;
+            case NestedArtboardLeafBase::fitPropertyKey:
+                object->as<NestedArtboardLeafBase>()->fit(value);
+                break;
             case WeightBase::valuesPropertyKey:
                 object->as<WeightBase>()->values(value);
                 break;
@@ -1466,6 +1469,12 @@
             case BindablePropertyNumberBase::propertyValuePropertyKey:
                 object->as<BindablePropertyNumberBase>()->propertyValue(value);
                 break;
+            case NestedArtboardLeafBase::alignmentXPropertyKey:
+                object->as<NestedArtboardLeafBase>()->alignmentX(value);
+                break;
+            case NestedArtboardLeafBase::alignmentYPropertyKey:
+                object->as<NestedArtboardLeafBase>()->alignmentY(value);
+                break;
             case BoneBase::lengthPropertyKey:
                 object->as<BoneBase>()->length(value);
                 break;
@@ -1721,10 +1730,6 @@
                 return object->as<DrawableBase>()->drawableFlags();
             case NestedArtboardBase::artboardIdPropertyKey:
                 return object->as<NestedArtboardBase>()->artboardId();
-            case NestedArtboardBase::fitPropertyKey:
-                return object->as<NestedArtboardBase>()->fit();
-            case NestedArtboardBase::alignmentPropertyKey:
-                return object->as<NestedArtboardBase>()->alignment();
             case NestedAnimationBase::animationIdPropertyKey:
                 return object->as<NestedAnimationBase>()->animationId();
             case SoloBase::activeComponentIdPropertyKey:
@@ -1929,6 +1934,8 @@
                 return object->as<DataBindBase>()->flags();
             case BindablePropertyEnumBase::propertyValuePropertyKey:
                 return object->as<BindablePropertyEnumBase>()->propertyValue();
+            case NestedArtboardLeafBase::fitPropertyKey:
+                return object->as<NestedArtboardLeafBase>()->fit();
             case WeightBase::valuesPropertyKey:
                 return object->as<WeightBase>()->values();
             case WeightBase::indicesPropertyKey:
@@ -2282,6 +2289,10 @@
                 return object->as<JoystickBase>()->height();
             case BindablePropertyNumberBase::propertyValuePropertyKey:
                 return object->as<BindablePropertyNumberBase>()->propertyValue();
+            case NestedArtboardLeafBase::alignmentXPropertyKey:
+                return object->as<NestedArtboardLeafBase>()->alignmentX();
+            case NestedArtboardLeafBase::alignmentYPropertyKey:
+                return object->as<NestedArtboardLeafBase>()->alignmentY();
             case BoneBase::lengthPropertyKey:
                 return object->as<BoneBase>()->length();
             case RootBoneBase::xPropertyKey:
@@ -2426,8 +2437,6 @@
             case DrawableBase::blendModeValuePropertyKey:
             case DrawableBase::drawableFlagsPropertyKey:
             case NestedArtboardBase::artboardIdPropertyKey:
-            case NestedArtboardBase::fitPropertyKey:
-            case NestedArtboardBase::alignmentPropertyKey:
             case NestedAnimationBase::animationIdPropertyKey:
             case SoloBase::activeComponentIdPropertyKey:
             case LayoutComponentStyleBase::scaleTypePropertyKey:
@@ -2530,6 +2539,7 @@
             case DataBindBase::propertyKeyPropertyKey:
             case DataBindBase::flagsPropertyKey:
             case BindablePropertyEnumBase::propertyValuePropertyKey:
+            case NestedArtboardLeafBase::fitPropertyKey:
             case WeightBase::valuesPropertyKey:
             case WeightBase::indicesPropertyKey:
             case TendonBase::boneIdPropertyKey:
@@ -2700,6 +2710,8 @@
             case JoystickBase::widthPropertyKey:
             case JoystickBase::heightPropertyKey:
             case BindablePropertyNumberBase::propertyValuePropertyKey:
+            case NestedArtboardLeafBase::alignmentXPropertyKey:
+            case NestedArtboardLeafBase::alignmentYPropertyKey:
             case BoneBase::lengthPropertyKey:
             case RootBoneBase::xPropertyKey:
             case RootBoneBase::yPropertyKey:
@@ -2872,10 +2884,6 @@
                 return object->is<DrawableBase>();
             case NestedArtboardBase::artboardIdPropertyKey:
                 return object->is<NestedArtboardBase>();
-            case NestedArtboardBase::fitPropertyKey:
-                return object->is<NestedArtboardBase>();
-            case NestedArtboardBase::alignmentPropertyKey:
-                return object->is<NestedArtboardBase>();
             case NestedAnimationBase::animationIdPropertyKey:
                 return object->is<NestedAnimationBase>();
             case SoloBase::activeComponentIdPropertyKey:
@@ -3080,6 +3088,8 @@
                 return object->is<DataBindBase>();
             case BindablePropertyEnumBase::propertyValuePropertyKey:
                 return object->is<BindablePropertyEnumBase>();
+            case NestedArtboardLeafBase::fitPropertyKey:
+                return object->is<NestedArtboardLeafBase>();
             case WeightBase::valuesPropertyKey:
                 return object->is<WeightBase>();
             case WeightBase::indicesPropertyKey:
@@ -3412,6 +3422,10 @@
                 return object->is<JoystickBase>();
             case BindablePropertyNumberBase::propertyValuePropertyKey:
                 return object->is<BindablePropertyNumberBase>();
+            case NestedArtboardLeafBase::alignmentXPropertyKey:
+                return object->is<NestedArtboardLeafBase>();
+            case NestedArtboardLeafBase::alignmentYPropertyKey:
+                return object->is<NestedArtboardLeafBase>();
             case BoneBase::lengthPropertyKey:
                 return object->is<BoneBase>();
             case RootBoneBase::xPropertyKey:
diff --git a/include/rive/generated/nested_artboard_base.hpp b/include/rive/generated/nested_artboard_base.hpp
index a115ba2..a0c203a 100644
--- a/include/rive/generated/nested_artboard_base.hpp
+++ b/include/rive/generated/nested_artboard_base.hpp
@@ -36,14 +36,10 @@
     uint16_t coreType() const override { return typeKey; }
 
     static const uint16_t artboardIdPropertyKey = 197;
-    static const uint16_t fitPropertyKey = 538;
-    static const uint16_t alignmentPropertyKey = 539;
-    static const uint16_t dataBindPathIdsPropertyKey = 580;
+    static const uint16_t dataBindPathIdsPropertyKey = 582;
 
 private:
     uint32_t m_ArtboardId = -1;
-    uint32_t m_Fit = 0;
-    uint32_t m_Alignment = 0;
 
 public:
     inline uint32_t artboardId() const { return m_ArtboardId; }
@@ -57,28 +53,6 @@
         artboardIdChanged();
     }
 
-    inline uint32_t fit() const { return m_Fit; }
-    void fit(uint32_t value)
-    {
-        if (m_Fit == value)
-        {
-            return;
-        }
-        m_Fit = value;
-        fitChanged();
-    }
-
-    inline uint32_t alignment() const { return m_Alignment; }
-    void alignment(uint32_t value)
-    {
-        if (m_Alignment == value)
-        {
-            return;
-        }
-        m_Alignment = value;
-        alignmentChanged();
-    }
-
     virtual void decodeDataBindPathIds(Span<const uint8_t> value) = 0;
     virtual void copyDataBindPathIds(const NestedArtboardBase& object) = 0;
 
@@ -86,8 +60,6 @@
     void copy(const NestedArtboardBase& object)
     {
         m_ArtboardId = object.m_ArtboardId;
-        m_Fit = object.m_Fit;
-        m_Alignment = object.m_Alignment;
         copyDataBindPathIds(object);
         Drawable::copy(object);
     }
@@ -99,12 +71,6 @@
             case artboardIdPropertyKey:
                 m_ArtboardId = CoreUintType::deserialize(reader);
                 return true;
-            case fitPropertyKey:
-                m_Fit = CoreUintType::deserialize(reader);
-                return true;
-            case alignmentPropertyKey:
-                m_Alignment = CoreUintType::deserialize(reader);
-                return true;
             case dataBindPathIdsPropertyKey:
                 decodeDataBindPathIds(CoreBytesType::deserialize(reader));
                 return true;
@@ -114,8 +80,6 @@
 
 protected:
     virtual void artboardIdChanged() {}
-    virtual void fitChanged() {}
-    virtual void alignmentChanged() {}
     virtual void dataBindPathIdsChanged() {}
 };
 } // namespace rive
diff --git a/include/rive/generated/nested_artboard_layout_base.hpp b/include/rive/generated/nested_artboard_layout_base.hpp
new file mode 100644
index 0000000..7c5ce6f
--- /dev/null
+++ b/include/rive/generated/nested_artboard_layout_base.hpp
@@ -0,0 +1,42 @@
+#ifndef _RIVE_NESTED_ARTBOARD_LAYOUT_BASE_HPP_
+#define _RIVE_NESTED_ARTBOARD_LAYOUT_BASE_HPP_
+#include "rive/nested_artboard.hpp"
+namespace rive
+{
+class NestedArtboardLayoutBase : public NestedArtboard
+{
+protected:
+    typedef NestedArtboard Super;
+
+public:
+    static const uint16_t typeKey = 452;
+
+    /// Helper to quickly determine if a core object extends another without RTTI
+    /// at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case NestedArtboardLayoutBase::typeKey:
+            case NestedArtboardBase::typeKey:
+            case DrawableBase::typeKey:
+            case NodeBase::typeKey:
+            case TransformComponentBase::typeKey:
+            case WorldTransformComponentBase::typeKey:
+            case ContainerComponentBase::typeKey:
+            case ComponentBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    Core* clone() const override;
+
+protected:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/generated/nested_artboard_leaf_base.hpp b/include/rive/generated/nested_artboard_leaf_base.hpp
new file mode 100644
index 0000000..d72fa60
--- /dev/null
+++ b/include/rive/generated/nested_artboard_leaf_base.hpp
@@ -0,0 +1,114 @@
+#ifndef _RIVE_NESTED_ARTBOARD_LEAF_BASE_HPP_
+#define _RIVE_NESTED_ARTBOARD_LEAF_BASE_HPP_
+#include "rive/core/field_types/core_double_type.hpp"
+#include "rive/core/field_types/core_uint_type.hpp"
+#include "rive/nested_artboard.hpp"
+namespace rive
+{
+class NestedArtboardLeafBase : public NestedArtboard
+{
+protected:
+    typedef NestedArtboard Super;
+
+public:
+    static const uint16_t typeKey = 451;
+
+    /// Helper to quickly determine if a core object extends another without RTTI
+    /// at runtime.
+    bool isTypeOf(uint16_t typeKey) const override
+    {
+        switch (typeKey)
+        {
+            case NestedArtboardLeafBase::typeKey:
+            case NestedArtboardBase::typeKey:
+            case DrawableBase::typeKey:
+            case NodeBase::typeKey:
+            case TransformComponentBase::typeKey:
+            case WorldTransformComponentBase::typeKey:
+            case ContainerComponentBase::typeKey:
+            case ComponentBase::typeKey:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    uint16_t coreType() const override { return typeKey; }
+
+    static const uint16_t fitPropertyKey = 538;
+    static const uint16_t alignmentXPropertyKey = 644;
+    static const uint16_t alignmentYPropertyKey = 645;
+
+private:
+    uint32_t m_Fit = 0;
+    float m_AlignmentX = 0.0f;
+    float m_AlignmentY = 0.0f;
+
+public:
+    inline uint32_t fit() const { return m_Fit; }
+    void fit(uint32_t value)
+    {
+        if (m_Fit == value)
+        {
+            return;
+        }
+        m_Fit = value;
+        fitChanged();
+    }
+
+    inline float alignmentX() const { return m_AlignmentX; }
+    void alignmentX(float value)
+    {
+        if (m_AlignmentX == value)
+        {
+            return;
+        }
+        m_AlignmentX = value;
+        alignmentXChanged();
+    }
+
+    inline float alignmentY() const { return m_AlignmentY; }
+    void alignmentY(float value)
+    {
+        if (m_AlignmentY == value)
+        {
+            return;
+        }
+        m_AlignmentY = value;
+        alignmentYChanged();
+    }
+
+    Core* clone() const override;
+    void copy(const NestedArtboardLeafBase& object)
+    {
+        m_Fit = object.m_Fit;
+        m_AlignmentX = object.m_AlignmentX;
+        m_AlignmentY = object.m_AlignmentY;
+        NestedArtboard::copy(object);
+    }
+
+    bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+    {
+        switch (propertyKey)
+        {
+            case fitPropertyKey:
+                m_Fit = CoreUintType::deserialize(reader);
+                return true;
+            case alignmentXPropertyKey:
+                m_AlignmentX = CoreDoubleType::deserialize(reader);
+                return true;
+            case alignmentYPropertyKey:
+                m_AlignmentY = CoreDoubleType::deserialize(reader);
+                return true;
+        }
+        return NestedArtboard::deserialize(propertyKey, reader);
+    }
+
+protected:
+    virtual void fitChanged() {}
+    virtual void alignmentXChanged() {}
+    virtual void alignmentYChanged() {}
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/layout_component.hpp b/include/rive/layout_component.hpp
index 976544c..c0c4bdb 100644
--- a/include/rive/layout_component.hpp
+++ b/include/rive/layout_component.hpp
@@ -12,7 +12,7 @@
 #include "yoga/YGStyle.h"
 #include "yoga/Yoga.h"
 #endif
-#include <stdio.h>
+
 namespace rive
 {
 
@@ -57,15 +57,12 @@
     Artboard* getArtboard() override { return artboard(); }
 
 private:
-    virtual void performUpdate(ComponentDirt value);
-
 #ifdef WITH_RIVE_LAYOUT
-private:
+protected:
     YGNode& layoutNode() { return m_layoutData->node; }
     YGStyle& layoutStyle() { return m_layoutData->style; }
     void syncLayoutChildren();
     void propagateSizeToChildren(ContainerComponent* component);
-    AABB findMaxIntrinsicSize(ContainerComponent* component, AABB maxIntrinsicSize);
     bool applyInterpolation(double elapsedSeconds);
 
 protected:
@@ -75,11 +72,25 @@
 public:
     LayoutComponentStyle* style() { return m_style; }
     void style(LayoutComponentStyle* style) { m_style = style; }
+
     void draw(Renderer* renderer) override;
     void drawProxy(Renderer* renderer) override;
     Core* hitTest(HitInfo*, const Mat2D&) override;
     DrawableProxy* proxy() { return &m_proxy; };
+    virtual void updateRenderPath();
     void update(ComponentDirt value) override;
+    void onDirty(ComponentDirt value) override;
+    AABB layoutBounds()
+    {
+        return AABB::fromLTWH(m_layoutLocationX,
+                              m_layoutLocationY,
+                              m_layoutSizeWidth,
+                              m_layoutSizeHeight);
+    }
+    AABB localBounds() const override
+    {
+        return AABB::fromLTWH(0.0f, 0.0f, m_layoutSizeWidth, m_layoutSizeHeight);
+    }
 
 #ifdef WITH_RIVE_LAYOUT
     LayoutComponent() : m_layoutData(std::unique_ptr<LayoutData>(new LayoutData())), m_proxy(this)
@@ -91,6 +102,7 @@
     virtual void propagateSize();
     void updateLayoutBounds();
     StatusCode onAddedDirty(CoreContext* context) override;
+    StatusCode onAddedClean(CoreContext* context) override;
 
     bool advance(double elapsedSeconds);
     bool animates();
@@ -106,22 +118,9 @@
                                    KeyFrameInterpolator* inheritedInterpolator,
                                    float inheritedInterpolationTime);
     void clearInheritedInterpolation();
-    virtual AABB layoutBounds()
-    {
-        return AABB(m_layoutLocationX,
-                    m_layoutLocationY,
-                    m_layoutLocationX + m_layoutSizeWidth,
-                    m_layoutLocationY + m_layoutSizeHeight);
-    };
-    bool hasLayoutMeasurements()
-    {
-        return m_layoutLocationX != 0 || m_layoutLocationY != 0 || m_layoutSizeWidth != 0 ||
-               m_layoutSizeHeight != 0;
-    };
 #else
     LayoutComponent() : m_layoutData(std::unique_ptr<LayoutData>(new LayoutData())), m_proxy(this)
     {}
-
 #endif
     void buildDependencies() override;
 
diff --git a/include/rive/nested_artboard.hpp b/include/rive/nested_artboard.hpp
index 5759815..6457727 100644
--- a/include/rive/nested_artboard.hpp
+++ b/include/rive/nested_artboard.hpp
@@ -11,30 +11,6 @@
 namespace rive
 {
 
-enum class NestedArtboardFitType : uint8_t
-{
-    fill, // Default value - scales to fill available view without maintaining aspect ratio
-    contain,
-    cover,
-    fitWidth,
-    fitHeight,
-    resizeArtboard,
-    none,
-};
-
-enum class NestedArtboardAlignmentType : uint8_t
-{
-    center, // Default value
-    topLeft,
-    topCenter,
-    topRight,
-    centerLeft,
-    centerRight,
-    bottomLeft,
-    bottomCenter,
-    bottomRight,
-};
-
 class ArtboardInstance;
 class NestedAnimation;
 class NestedInput;
@@ -42,13 +18,10 @@
 class StateMachineInstance;
 class NestedArtboard : public NestedArtboardBase
 {
-
-private:
+protected:
     Artboard* m_Artboard = nullptr;               // might point to m_Instance, and might not
     std::unique_ptr<ArtboardInstance> m_Instance; // may be null
     std::vector<NestedAnimation*> m_NestedAnimations;
-    float m_layoutScaleX = NAN;
-    float m_layoutScaleY = NAN;
 
 protected:
     std::vector<uint32_t> m_DataBindPathIdsBuffer;
@@ -62,8 +35,7 @@
     void addNestedAnimation(NestedAnimation* nestedAnimation);
 
     void nest(Artboard* artboard);
-
-    ArtboardInstance* artboard() { return m_Instance.get(); }
+    ArtboardInstance* artboardInstance() { return m_Instance.get(); }
 
     StatusCode import(ImportStack& importStack) override;
     Core* clone() const override;
@@ -77,14 +49,6 @@
     NestedInput* input(std::string name) const;
     NestedInput* input(std::string name, std::string stateMachineName) const;
 
-    NestedArtboardAlignmentType alignmentType() const
-    {
-        return (NestedArtboardAlignmentType)alignment();
-    }
-    NestedArtboardFitType fitType() const { return (NestedArtboardFitType)fit(); }
-    float effectiveScaleX() { return std::isnan(m_layoutScaleX) ? scaleX() : m_layoutScaleX; }
-    float effectiveScaleY() { return std::isnan(m_layoutScaleY) ? scaleY() : m_layoutScaleY; }
-
     Vec2D measureLayout(float width,
                         LayoutMeasureMode widthMode,
                         float height,
@@ -96,6 +60,7 @@
     /// nested within. Returns true when the conversion succeeds, and false
     /// when one is not possible.
     bool worldToLocal(Vec2D world, Vec2D* local);
+    void syncStyleChanges();
     void decodeDataBindPathIds(Span<const uint8_t> value) override;
     void copyDataBindPathIds(const NestedArtboardBase& object) override;
     std::vector<uint32_t> dataBindPathIds() { return m_DataBindPathIdsBuffer; };
diff --git a/include/rive/nested_artboard_layout.hpp b/include/rive/nested_artboard_layout.hpp
new file mode 100644
index 0000000..9a68074
--- /dev/null
+++ b/include/rive/nested_artboard_layout.hpp
@@ -0,0 +1,19 @@
+#ifndef _RIVE_NESTED_ARTBOARD_LAYOUT_HPP_
+#define _RIVE_NESTED_ARTBOARD_LAYOUT_HPP_
+#include "rive/generated/nested_artboard_layout_base.hpp"
+
+namespace rive
+{
+class NestedArtboardLayout : public NestedArtboardLayoutBase
+{
+public:
+#ifdef WITH_RIVE_LAYOUT
+    void* layoutNode();
+#endif
+    Core* clone() const override;
+    void markNestedLayoutDirty();
+    void update(ComponentDirt value) override;
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/nested_artboard_leaf.hpp b/include/rive/nested_artboard_leaf.hpp
new file mode 100644
index 0000000..a7fa3d5
--- /dev/null
+++ b/include/rive/nested_artboard_leaf.hpp
@@ -0,0 +1,15 @@
+#ifndef _RIVE_NESTED_ARTBOARD_LEAF_HPP_
+#define _RIVE_NESTED_ARTBOARD_LEAF_HPP_
+#include "rive/generated/nested_artboard_leaf_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+class NestedArtboardLeaf : public NestedArtboardLeafBase
+{
+public:
+    Core* clone() const override;
+    void update(ComponentDirt value) override;
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/transform_component.hpp b/include/rive/transform_component.hpp
index 151c31b..36580a6 100644
--- a/include/rive/transform_component.hpp
+++ b/include/rive/transform_component.hpp
@@ -12,7 +12,7 @@
 class AABB;
 class TransformComponent : public TransformComponentBase
 {
-private:
+protected:
     Mat2D m_Transform;
     float m_RenderOpacity = 0.0f;
     WorldTransformComponent* m_ParentTransformComponent = nullptr;
@@ -27,7 +27,7 @@
     StatusCode onAddedClean(CoreContext* context) override;
     void buildDependencies() override;
     void update(ComponentDirt value) override;
-    void updateTransform();
+    virtual void updateTransform();
     virtual void updateWorldTransform();
     void markTransformDirty();
 
diff --git a/src/artboard.cpp b/src/artboard.cpp
index 7cd9b02..263f683 100644
--- a/src/artboard.cpp
+++ b/src/artboard.cpp
@@ -17,6 +17,8 @@
 #include "rive/importers/backboard_importer.hpp"
 #include "rive/layout_component.hpp"
 #include "rive/nested_artboard.hpp"
+#include "rive/nested_artboard_leaf.hpp"
+#include "rive/nested_artboard_layout.hpp"
 #include "rive/joystick.hpp"
 #include "rive/data_bind_flags.hpp"
 #include "rive/animation/nested_bool.hpp"
@@ -33,6 +35,8 @@
 
 using namespace rive;
 
+Artboard::Artboard() {}
+
 Artboard::~Artboard()
 {
 #ifdef WITH_RIVE_AUDIO
@@ -172,6 +176,8 @@
                 break;
             }
             case NestedArtboardBase::typeKey:
+            case NestedArtboardLeafBase::typeKey:
+            case NestedArtboardLayoutBase::typeKey:
                 m_NestedArtboards.push_back(object->as<NestedArtboard>());
                 break;
 
@@ -336,6 +342,10 @@
         m_DrawTargets.push_back(static_cast<DrawTarget*>(*itr++));
     }
 
+    // Some default layout dimensions.
+    m_layoutSizeWidth = width();
+    m_layoutSizeHeight = height();
+
     return StatusCode::Ok;
 }
 
@@ -490,6 +500,10 @@
 void Artboard::propagateSize()
 {
     addDirt(ComponentDirt::Path);
+    if (sharesLayoutWithHost())
+    {
+        m_host->markTransformDirty();
+    }
 #ifdef WITH_RIVE_TOOLS
     if (m_layoutChangedCallback != nullptr)
     {
@@ -499,6 +513,38 @@
 }
 #endif
 
+bool Artboard::sharesLayoutWithHost() const
+{
+    return m_host != nullptr && m_host->is<NestedArtboardLayout>();
+}
+void Artboard::host(NestedArtboard* nestedArtboard)
+{
+    m_host = nestedArtboard;
+#ifdef WITH_RIVE_LAYOUT
+    if (!sharesLayoutWithHost())
+    {
+        return;
+    }
+    Artboard* parent = parentArtboard();
+    if (parent != nullptr)
+    {
+        parent->markLayoutDirty(this);
+        parent->syncLayoutChildren();
+    }
+#endif
+}
+
+NestedArtboard* Artboard::host() const { return m_host; }
+
+Artboard* Artboard::parentArtboard() const
+{
+    if (m_host == nullptr)
+    {
+        return nullptr;
+    }
+    return m_host->artboard();
+}
+
 float Artboard::layoutWidth() const
 {
 #ifdef WITH_RIVE_LAYOUT
@@ -517,37 +563,53 @@
 #endif
 }
 
-void Artboard::performUpdate(ComponentDirt value)
+float Artboard::layoutX() const
 {
+#ifdef WITH_RIVE_LAYOUT
+    return m_layoutLocationX;
+#else
+    return 0.0f;
+#endif
+}
+
+float Artboard::layoutY() const
+{
+#ifdef WITH_RIVE_LAYOUT
+    return m_layoutLocationY;
+#else
+    return 0.0f;
+#endif
+}
+
+void Artboard::updateRenderPath()
+{
+    AABB bg = AABB::fromLTWH(-layoutWidth() * originX(),
+                             -layoutHeight() * originY(),
+                             layoutWidth(),
+                             layoutHeight());
+    AABB clip;
+    if (m_FrameOrigin)
+    {
+        clip = {0.0f, 0.0f, layoutWidth(), layoutHeight()};
+    }
+    else
+    {
+        clip = bg;
+    }
+    m_clipPath = factory()->makeRenderPath(clip);
+    m_backgroundRawPath.rewind();
+    m_backgroundRawPath.addRect(bg);
+    m_backgroundPath->rewind();
+    m_backgroundRawPath.addTo(m_backgroundPath.get());
+}
+
+void Artboard::update(ComponentDirt value)
+{
+    Super::update(value);
     if (hasDirt(value, ComponentDirt::DrawOrder))
     {
         sortDrawOrder();
     }
-    if (hasDirt(value, ComponentDirt::Path))
-    {
-        AABB bg = AABB::fromLTWH(-layoutWidth() * originX(),
-                                 -layoutHeight() * originY(),
-                                 layoutWidth(),
-                                 layoutHeight());
-        AABB clip;
-        if (m_FrameOrigin)
-        {
-            clip = {0.0f, 0.0f, layoutWidth(), layoutHeight()};
-        }
-        else
-        {
-            clip = bg;
-        }
-        m_clipPath = factory()->makeRenderPath(clip);
-
-        m_backgroundRawPath.addRect(bg);
-        m_backgroundPath->rewind();
-        m_backgroundRawPath.addTo(m_backgroundPath.get());
-    }
-    if (hasDirt(value, ComponentDirt::RenderOpacity))
-    {
-        propagateOpacity(childOpacity());
-    }
 }
 
 void Artboard::updateDataBinds()
@@ -606,38 +668,97 @@
     return false;
 }
 
+void* Artboard::takeLayoutNode()
+{
+#ifdef WITH_RIVE_LAYOUT
+    m_updatesOwnLayout = false;
+    return static_cast<void*>(&layoutNode());
+#else
+    return nullptr;
+#endif
+}
+
+void Artboard::markLayoutDirty(LayoutComponent* layoutComponent)
+{
+#ifdef WITH_RIVE_TOOLS
+    if (m_dirtyLayout.empty() && m_layoutDirtyCallback != nullptr)
+    {
+        m_layoutDirtyCallback(this);
+    }
+#endif
+    m_dirtyLayout.insert(layoutComponent);
+    if (sharesLayoutWithHost())
+    {
+        m_host->as<NestedArtboardLayout>()->markNestedLayoutDirty();
+    }
+}
+
+bool Artboard::syncStyleChanges()
+{
+    bool updated = false;
+#ifdef WITH_RIVE_LAYOUT
+    if (!m_dirtyLayout.empty())
+    {
+        for (auto layout : m_dirtyLayout)
+        {
+            switch (layout->coreType())
+            {
+                case ArtboardBase::typeKey:
+                {
+                    auto artboard = layout->as<Artboard>();
+                    if (artboard == this)
+                    {
+                        artboard->syncStyle();
+                    }
+                    else
+                    {
+                        // This is a nested artboard, sync its changes too.
+                        artboard->syncStyleChanges();
+                    }
+                    break;
+                }
+
+                default:
+                    layout->syncStyle();
+                    break;
+            }
+        }
+        m_dirtyLayout.clear();
+        updated = true;
+    }
+#endif
+    return updated;
+}
+
 bool Artboard::advanceInternal(double elapsedSeconds, bool isRoot, bool nested)
 {
     bool didUpdate = false;
     m_HasChangedDrawOrderInLastUpdate = false;
 #ifdef WITH_RIVE_LAYOUT
-    if (!m_dirtyLayout.empty())
+    if (hasDirt(ComponentDirt::LayoutStyle))
     {
-        syncStyle();
-        for (auto layout : m_dirtyLayout)
-        {
-            layout->syncStyle();
-        }
-        m_dirtyLayout.clear();
+        cascadeAnimationStyle(interpolation(), interpolator(), interpolationTime());
+    }
+
+    if (syncStyleChanges() && m_updatesOwnLayout)
+    {
         calculateLayout();
-        if (hasDirt(ComponentDirt::LayoutStyle))
+    }
+
+    for (auto dep : m_DependencyOrder)
+    {
+        if (dep->is<LayoutComponent>())
         {
-            cascadeAnimationStyle(interpolation(), interpolator(), interpolationTime());
-        }
-        for (auto dep : m_DependencyOrder)
-        {
-            if (dep->is<LayoutComponent>())
+            auto layout = dep->as<LayoutComponent>();
+            layout->updateLayoutBounds();
+            if ((dep == this && Super::advance(elapsedSeconds)) ||
+                (dep != this && layout->advance(elapsedSeconds)))
             {
-                auto layout = dep->as<LayoutComponent>();
-                layout->updateLayoutBounds();
-                if ((dep == this && Super::advance(elapsedSeconds)) ||
-                    (dep != this && layout->advance(elapsedSeconds)))
-                {
-                    didUpdate = true;
-                }
+                didUpdate = true;
             }
         }
     }
+
 #endif
     if (m_JoysticksApplyBeforeUpdate)
     {
@@ -791,6 +912,12 @@
     }
 }
 
+Vec2D Artboard::origin() const
+{
+    return m_FrameOrigin ? Vec2D(0.0f, 0.0f)
+                         : Vec2D(-layoutWidth() * originX(), -layoutHeight() * originY());
+}
+
 AABB Artboard::bounds() const
 {
     return m_FrameOrigin ? AABB(0.0f, 0.0f, layoutWidth(), layoutHeight())
@@ -823,7 +950,7 @@
     }
     for (auto nestedArtboard : m_NestedArtboards)
     {
-        if (nestedArtboard->artboard()->hasAudio())
+        if (nestedArtboard->artboardInstance()->hasAudio())
         {
             return true;
         }
@@ -954,7 +1081,7 @@
             }
             else
             {
-                auto artboard = nested->artboard();
+                auto artboard = nested->artboardInstance();
                 return artboard->nestedArtboardAtPath(restOfPath);
             }
         }
@@ -1039,7 +1166,7 @@
     m_DataContext->parent(parent);
     for (auto nestedArtboard : m_NestedArtboards)
     {
-        if (nestedArtboard->artboard() == nullptr)
+        if (nestedArtboard->artboardInstance() == nullptr)
         {
             continue;
         }
@@ -1084,7 +1211,7 @@
     m_volume = value;
     for (auto nestedArtboard : m_NestedArtboards)
     {
-        auto artboard = nestedArtboard->artboard();
+        auto artboard = nestedArtboard->artboardInstance();
         if (artboard != nullptr)
         {
             artboard->volume(value);
@@ -1100,9 +1227,9 @@
     }
     for (auto nestedArtboard : m_NestedArtboards)
     {
-        if (nestedArtboard->artboard() != nullptr)
+        if (nestedArtboard->artboardInstance() != nullptr)
         {
-            nestedArtboard->artboard()->populateDataBinds(dataBinds);
+            nestedArtboard->artboardInstance()->populateDataBinds(dataBinds);
         }
     }
 }
@@ -1236,7 +1363,7 @@
     m_audioEngine = audioEngine;
     for (auto nestedArtboard : m_NestedArtboards)
     {
-        auto artboard = nestedArtboard->artboard();
+        auto artboard = nestedArtboard->artboardInstance();
         if (artboard != nullptr)
         {
             artboard->audioEngine(audioEngine);
diff --git a/src/generated/nested_artboard_layout_base.cpp b/src/generated/nested_artboard_layout_base.cpp
new file mode 100644
index 0000000..069c374
--- /dev/null
+++ b/src/generated/nested_artboard_layout_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/nested_artboard_layout_base.hpp"
+#include "rive/nested_artboard_layout.hpp"
+
+using namespace rive;
+
+Core* NestedArtboardLayoutBase::clone() const
+{
+    auto cloned = new NestedArtboardLayout();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/src/generated/nested_artboard_leaf_base.cpp b/src/generated/nested_artboard_leaf_base.cpp
new file mode 100644
index 0000000..d025a17
--- /dev/null
+++ b/src/generated/nested_artboard_leaf_base.cpp
@@ -0,0 +1,11 @@
+#include "rive/generated/nested_artboard_leaf_base.hpp"
+#include "rive/nested_artboard_leaf.hpp"
+
+using namespace rive;
+
+Core* NestedArtboardLeafBase::clone() const
+{
+    auto cloned = new NestedArtboardLeaf();
+    cloned->copy(*this);
+    return cloned;
+}
diff --git a/src/layout_component.cpp b/src/layout_component.cpp
index 52040d4..872e88e 100644
--- a/src/layout_component.cpp
+++ b/src/layout_component.cpp
@@ -9,6 +9,7 @@
 #include "rive/shapes/paint/shape_paint.hpp"
 #include "rive/shapes/paint/stroke.hpp"
 #include "rive/shapes/rectangle.hpp"
+#include "rive/nested_artboard_layout.hpp"
 #ifdef WITH_RIVE_LAYOUT
 #include "rive/transform_component.hpp"
 #include "yoga/YGEnums.h"
@@ -82,51 +83,52 @@
 
 Core* LayoutComponent::hitTest(HitInfo*, const Mat2D&) { return nullptr; }
 
+void LayoutComponent::updateRenderPath()
+{
+    m_backgroundRect->width(m_layoutSizeWidth);
+    m_backgroundRect->height(m_layoutSizeHeight);
+    m_backgroundRect->linkCornerRadius(style()->linkCornerRadius());
+    m_backgroundRect->cornerRadiusTL(style()->cornerRadiusTL());
+    m_backgroundRect->cornerRadiusTR(style()->cornerRadiusTR());
+    m_backgroundRect->cornerRadiusBL(style()->cornerRadiusBL());
+    m_backgroundRect->cornerRadiusBR(style()->cornerRadiusBR());
+    m_backgroundRect->update(ComponentDirt::Path);
+
+    m_backgroundPath->rewind();
+    m_backgroundRect->rawPath().addTo(m_backgroundPath.get());
+
+    RawPath clipPath;
+    clipPath.addPath(m_backgroundRect->rawPath(), &m_WorldTransform);
+    m_clipPath = artboard()->factory()->makeRenderPath(clipPath, FillRule::nonZero);
+}
+
 void LayoutComponent::update(ComponentDirt value)
 {
     Super::update(value);
-    performUpdate(value);
-}
-
-void LayoutComponent::performUpdate(ComponentDirt value)
-{
     if (hasDirt(value, ComponentDirt::RenderOpacity))
     {
-        propagateOpacity(renderOpacity());
+        propagateOpacity(childOpacity());
     }
-    if (hasDirt(value, ComponentDirt::Path))
-    {
-        m_backgroundRect->width(m_layoutSizeWidth);
-        m_backgroundRect->height(m_layoutSizeHeight);
-        m_backgroundRect->linkCornerRadius(style()->linkCornerRadius());
-        m_backgroundRect->cornerRadiusTL(style()->cornerRadiusTL());
-        m_backgroundRect->cornerRadiusTR(style()->cornerRadiusTR());
-        m_backgroundRect->cornerRadiusBL(style()->cornerRadiusBL());
-        m_backgroundRect->cornerRadiusBR(style()->cornerRadiusBR());
-        m_backgroundRect->update(value);
-
-        m_backgroundPath->rewind();
-        m_backgroundRect->rawPath().addTo(m_backgroundPath.get());
-        AABB clipBounds = AABB::fromLTWH(worldTranslation().x,
-                                         worldTranslation().y,
-                                         worldTranslation().x + m_layoutSizeWidth,
-                                         worldTranslation().y + m_layoutSizeHeight);
-        m_clipPath = artboard()->factory()->makeRenderPath(clipBounds);
-    }
-    if (hasDirt(value, ComponentDirt::WorldTransform))
+    if (parent() != nullptr && hasDirt(value, ComponentDirt::WorldTransform))
     {
         Mat2D parentWorld = parent()->is<WorldTransformComponent>()
                                 ? (parent()->as<WorldTransformComponent>())->worldTransform()
                                 : Mat2D();
-        auto transform = Mat2D();
-        transform[4] = m_layoutLocationX;
-        transform[5] = m_layoutLocationY;
-
-        auto multipliedTransform = Mat2D::multiply(parentWorld, transform);
-        m_WorldTransform = multipliedTransform;
-
+        auto location = Vec2D(m_layoutLocationX, m_layoutLocationY);
+        if (parent()->is<Artboard>())
+        {
+            auto art = parent()->as<Artboard>();
+            location -=
+                Vec2D(art->layoutWidth() * art->originX(), art->layoutHeight() * art->originY());
+        }
+        auto transform = Mat2D::fromTranslation(location);
+        m_WorldTransform = Mat2D::multiply(parentWorld, transform);
         updateConstraints();
     }
+    if (hasDirt(value, ComponentDirt::Path))
+    {
+        updateRenderPath();
+    }
 }
 
 #ifdef WITH_RIVE_LAYOUT
@@ -145,16 +147,24 @@
     }
     m_style = static_cast<LayoutComponentStyle*>(coreStyle);
     addChild(m_style);
+
+    return StatusCode::Ok;
+}
+
+StatusCode LayoutComponent::onAddedClean(CoreContext* context)
+{
+    auto code = Super::onAddedClean(context);
+    if (code != StatusCode::Ok)
+    {
+        return code;
+    }
     artboard()->markLayoutDirty(this);
     markLayoutStyleDirty();
-    if (parent() != nullptr && parent()->is<LayoutComponent>())
-    {
-        parent()->as<LayoutComponent>()->syncLayoutChildren();
-    }
     m_backgroundPath = artboard()->factory()->makeEmptyRenderPath();
     m_clipPath = artboard()->factory()->makeEmptyRenderPath();
     m_backgroundRect->originX(0);
     m_backgroundRect->originY(0);
+    syncLayoutChildren();
     return StatusCode::Ok;
 }
 
@@ -185,6 +195,7 @@
         {
             continue;
         }
+        //  && child->is<TransformComponent>()->canMeasure() for nested artboard layout
         if (child->is<TransformComponent>())
         {
             auto transformComponent = child->as<TransformComponent>();
@@ -368,27 +379,33 @@
 
 void LayoutComponent::syncLayoutChildren()
 {
-    YGNodeRemoveAllChildren(&layoutNode());
+    auto ourNode = &layoutNode();
+    YGNodeRemoveAllChildren(ourNode);
     int index = 0;
-    for (size_t i = 0; i < children().size(); i++)
+    for (auto child : children())
     {
-        Component* child = children()[i];
-        if (child->is<LayoutComponent>())
+        YGNode* node = nullptr;
+        switch (child->coreType())
         {
-            YGNodeInsertChild(&layoutNode(), &child->as<LayoutComponent>()->layoutNode(), index);
-            index += 1;
+            case LayoutComponentBase::typeKey:
+                node = &child->as<LayoutComponent>()->layoutNode();
+                break;
+            case NestedArtboardLayoutBase::typeKey:
+                node = static_cast<YGNode*>(child->as<NestedArtboardLayout>()->layoutNode());
+                break;
+        }
+        if (node != nullptr)
+        {
+            // YGNodeInsertChild(ourNode, node, index++);
+            ourNode->insertChild(node, index++);
+            node->setOwner(ourNode);
+            ourNode->markDirtyAndPropagate();
         }
     }
+    markLayoutNodeDirty();
 }
 
-void LayoutComponent::propagateSize()
-{
-    if (artboard() == this)
-    {
-        return;
-    }
-    propagateSizeToChildren(this);
-}
+void LayoutComponent::propagateSize() { propagateSizeToChildren(this); }
 
 void LayoutComponent::propagateSizeToChildren(ContainerComponent* component)
 {
@@ -415,6 +432,15 @@
     YGNodeCalculateLayout(&layoutNode(), width(), height(), YGDirection::YGDirectionInherit);
 }
 
+void LayoutComponent::onDirty(ComponentDirt value)
+{
+    Super::onDirty(value);
+    if ((value & ComponentDirt::WorldTransform) == ComponentDirt::WorldTransform && clip())
+    {
+        addDirt(ComponentDirt::Path);
+    }
+}
+
 void LayoutComponent::updateLayoutBounds()
 {
     auto node = &layoutNode();
@@ -422,6 +448,22 @@
     auto top = YGNodeLayoutGetTop(node);
     auto width = YGNodeLayoutGetWidth(node);
     auto height = YGNodeLayoutGetHeight(node);
+
+#ifdef DEBUG
+    // Temporarily here to keep track of an issue.
+    if (left != left || top != top || width != width || height != height)
+    {
+        fprintf(stderr,
+                "Layout returned nan: %f %f %f %f | %p %s\n",
+                left,
+                top,
+                width,
+                height,
+                YGNodeGetParent(node),
+                name().c_str());
+        return;
+    }
+#endif
     if (animates())
     {
         auto toBounds = m_animationData.toBounds;
@@ -438,13 +480,21 @@
             markWorldTransformDirty();
         }
     }
-    else if (left != m_layoutLocationX || top != m_layoutLocationY || width != m_layoutSizeWidth ||
-             height != m_layoutSizeHeight)
+    else
+
+        if (left != m_layoutLocationX || top != m_layoutLocationY || width != m_layoutSizeWidth ||
+            height != m_layoutSizeHeight)
     {
+        if (m_layoutSizeWidth != width || m_layoutSizeHeight != height)
+        {
+            // Width changed, we need to rebuild the path.
+            addDirt(ComponentDirt::Path);
+        }
         m_layoutLocationX = left;
         m_layoutLocationY = top;
         m_layoutSizeWidth = width;
         m_layoutSizeHeight = height;
+
         propagateSize();
         markWorldTransformDirty();
     }
@@ -572,14 +622,25 @@
     {
         return false;
     }
+
     if (m_animationData.elapsedSeconds >= interpolationTime())
     {
         m_layoutLocationX = m_animationData.toBounds.left();
         m_layoutLocationY = m_animationData.toBounds.top();
-        m_layoutSizeWidth = m_animationData.toBounds.width();
-        m_layoutSizeHeight = m_animationData.toBounds.height();
+
+        float width = m_animationData.toBounds.width();
+        float height = m_animationData.toBounds.height();
+        if (width != m_layoutSizeWidth || height != m_layoutSizeHeight)
+        {
+            addDirt(ComponentDirt::Path);
+        }
+        m_layoutSizeWidth = width;
+        m_layoutSizeHeight = height;
+
         m_animationData.elapsedSeconds = 0;
+        propagateSize();
         markWorldTransformDirty();
+
         return false;
     }
     float f = 1;
@@ -631,13 +692,13 @@
         needsAdvance = true;
         m_layoutSizeWidth = width;
         m_layoutSizeHeight = height;
+        addDirt(ComponentDirt::Path);
     }
     m_animationData.elapsedSeconds = m_animationData.elapsedSeconds + (float)elapsedSeconds;
     if (needsAdvance)
     {
         propagateSize();
         markWorldTransformDirty();
-        markLayoutNodeDirty();
     }
     return needsAdvance;
 }
@@ -668,9 +729,10 @@
 
 void LayoutComponent::markLayoutNodeDirty() {}
 void LayoutComponent::markLayoutStyleDirty() {}
+void LayoutComponent::onDirty(ComponentDirt value) {}
 #endif
 
 void LayoutComponent::clipChanged() { markLayoutNodeDirty(); }
 void LayoutComponent::widthChanged() { markLayoutNodeDirty(); }
 void LayoutComponent::heightChanged() { markLayoutNodeDirty(); }
-void LayoutComponent::styleIdChanged() { markLayoutNodeDirty(); }
\ No newline at end of file
+void LayoutComponent::styleIdChanged() { markLayoutNodeDirty(); }
diff --git a/src/nested_artboard.cpp b/src/nested_artboard.cpp
index 95138b0..6f30c4c 100644
--- a/src/nested_artboard.cpp
+++ b/src/nested_artboard.cpp
@@ -45,7 +45,9 @@
     {
         m_Instance.reset(static_cast<ArtboardInstance*>(artboard)); // take ownership
     }
-    m_Artboard->advanceInternal(0.0f, false);
+    // This allows for swapping after initial load (after onAddedClean has
+    // already been called).
+    m_Artboard->host(this);
 }
 
 static Mat2D makeTranslate(const Artboard* artboard)
@@ -124,6 +126,7 @@
         {
             animation->initializeAnimation(m_Instance.get());
         }
+        m_Artboard->host(this);
     }
     return Super::onAddedClean(context);
 }
@@ -247,19 +250,17 @@
                  m_Instance ? m_Instance->height() : 0.0f));
 }
 
-void NestedArtboard::controlSize(Vec2D size)
+void NestedArtboard::syncStyleChanges()
 {
-    auto newScaleX = size.x / m_Artboard->originalWidth();
-    auto newScaleY = size.y / m_Artboard->originalHeight();
-    if (newScaleX != scaleX() || newScaleY != scaleY())
+    if (m_Artboard == nullptr)
     {
-        // TODO: Support nested artboard fit & alignment
-        scaleX(newScaleX);
-        scaleY(newScaleY);
-        addDirt(ComponentDirt::WorldTransform, false);
+        return;
     }
+    m_Artboard->syncStyleChanges();
 }
 
+void NestedArtboard::controlSize(Vec2D size) {}
+
 void NestedArtboard::decodeDataBindPathIds(Span<const uint8_t> value)
 {
     BinaryReader reader(value);
@@ -277,7 +278,7 @@
 
 void NestedArtboard::internalDataContext(DataContext* value, DataContext* parent)
 {
-    artboard()->internalDataContext(value, parent, false);
+    artboardInstance()->internalDataContext(value, parent, false);
     for (auto animation : m_NestedAnimations)
     {
         if (animation->is<NestedStateMachine>())
@@ -290,7 +291,7 @@
 void NestedArtboard::dataContextFromInstance(ViewModelInstance* viewModelInstance,
                                              DataContext* parent)
 {
-    artboard()->dataContextFromInstance(viewModelInstance, parent, false);
+    artboardInstance()->dataContextFromInstance(viewModelInstance, parent, false);
     for (auto animation : m_NestedAnimations)
     {
         if (animation->is<NestedStateMachine>())
diff --git a/src/nested_artboard_layout.cpp b/src/nested_artboard_layout.cpp
new file mode 100644
index 0000000..eed115e
--- /dev/null
+++ b/src/nested_artboard_layout.cpp
@@ -0,0 +1,60 @@
+#include "rive/nested_artboard_layout.hpp"
+#include "rive/artboard.hpp"
+
+using namespace rive;
+
+Core* NestedArtboardLayout::clone() const
+{
+    NestedArtboardLayout* nestedArtboard =
+        static_cast<NestedArtboardLayout*>(NestedArtboardLayoutBase::clone());
+    if (m_Artboard == nullptr)
+    {
+        return nestedArtboard;
+    }
+    auto ni = m_Artboard->instance();
+    nestedArtboard->nest(ni.release());
+    return nestedArtboard;
+}
+
+#ifdef WITH_RIVE_LAYOUT
+void* NestedArtboardLayout::layoutNode()
+{
+    if (artboardInstance() == nullptr)
+    {
+        return nullptr;
+    }
+    return artboardInstance()->takeLayoutNode();
+}
+#endif
+
+void NestedArtboardLayout::markNestedLayoutDirty()
+{
+    if (artboardInstance() != nullptr)
+    {
+        artboardInstance()->markLayoutNodeDirty();
+    }
+}
+
+void NestedArtboardLayout::update(ComponentDirt value)
+{
+    Super::update(value);
+    auto artboard = artboardInstance();
+    if (hasDirt(value, ComponentDirt::WorldTransform) && artboard != nullptr)
+    {
+        auto layoutPosition = Vec2D(artboard->layoutX(), artboard->layoutY());
+
+        if (parent()->is<Artboard>())
+        {
+            auto parentArtboard = parent()->as<Artboard>();
+            auto correctedArtboardSpace =
+                Mat2D::fromTranslation(parentArtboard->origin() + layoutPosition);
+            m_WorldTransform = correctedArtboardSpace * m_WorldTransform;
+        }
+        else
+        {
+            m_WorldTransform = Mat2D::fromTranslation(layoutPosition) * m_WorldTransform;
+        }
+        auto back = Mat2D::fromTranslation(-artboard->origin());
+        m_WorldTransform = back * m_WorldTransform;
+    }
+}
\ No newline at end of file
diff --git a/src/nested_artboard_leaf.cpp b/src/nested_artboard_leaf.cpp
new file mode 100644
index 0000000..2ae92b6
--- /dev/null
+++ b/src/nested_artboard_leaf.cpp
@@ -0,0 +1,40 @@
+#include "rive/nested_artboard_leaf.hpp"
+#include "rive/renderer.hpp"
+#include "rive/layout_component.hpp"
+#include "rive/artboard.hpp"
+
+using namespace rive;
+
+Core* NestedArtboardLeaf::clone() const
+{
+    NestedArtboardLeaf* nestedArtboard =
+        static_cast<NestedArtboardLeaf*>(NestedArtboardLeafBase::clone());
+    if (m_Artboard == nullptr)
+    {
+        return nestedArtboard;
+    }
+    auto ni = m_Artboard->instance();
+    nestedArtboard->nest(ni.release());
+    return nestedArtboard;
+}
+
+void NestedArtboardLeaf::update(ComponentDirt value)
+{
+    Super::update(value);
+    auto artboard = artboardInstance();
+    if (hasDirt(value, ComponentDirt::WorldTransform) && artboard != nullptr)
+    {
+        auto p = parent();
+
+        AABB bounds = p != nullptr && p->is<LayoutComponent>()
+                          ? p->as<LayoutComponent>()->localBounds()
+                          : AABB();
+
+        auto viewTransform = computeAlignment((Fit)fit(),
+                                              Alignment(alignmentX(), alignmentY()),
+                                              bounds,
+                                              artboard->bounds());
+
+        m_WorldTransform *= viewTransform;
+    }
+}
diff --git a/test/nested_artboard.cpp b/test/nested_artboard.cpp
index db0c21c..fa88626 100644
--- a/test/nested_artboard.cpp
+++ b/test/nested_artboard.cpp
@@ -27,13 +27,13 @@
     // if checking whether the time of each artboard has advanced
     // Unfortunately there is no way of accessing the time of the animations directly
     auto redNestedArtboard = stateMachine->artboard()->find<rive::NestedArtboard>("red-artboard");
-    auto redNestedArtboardArtboard = redNestedArtboard->artboard();
+    auto redNestedArtboardArtboard = redNestedArtboard->artboardInstance();
     auto movingShapes = redNestedArtboardArtboard->find<rive::Shape>();
     auto redRect = movingShapes.at(0);
     REQUIRE(redRect->x() > 50);
     auto greenNestedArtboard =
         stateMachine->artboard()->find<rive::NestedArtboard>("green-artboard");
-    auto greenNestedArtboardArtboard = greenNestedArtboard->artboard();
+    auto greenNestedArtboardArtboard = greenNestedArtboard->artboardInstance();
     auto greenMovingShapes = greenNestedArtboardArtboard->find<rive::Shape>();
     auto greenRect = greenMovingShapes.at(0);
     REQUIRE(greenRect->x() == 50);
diff --git a/test/nested_artboard_opacity_test.cpp b/test/nested_artboard_opacity_test.cpp
index 5fdd19e..209f209 100644
--- a/test/nested_artboard_opacity_test.cpp
+++ b/test/nested_artboard_opacity_test.cpp
@@ -16,8 +16,8 @@
     REQUIRE(artboard->find("Nested artboard container") != nullptr);
     auto nestedArtboardContainer =
         artboard->find<rive::NestedArtboard>("Nested artboard container");
-    REQUIRE(nestedArtboardContainer->artboard() != nullptr);
-    auto nestedArtboard = nestedArtboardContainer->artboard();
+    REQUIRE(nestedArtboardContainer->artboardInstance() != nullptr);
+    auto nestedArtboard = nestedArtboardContainer->artboardInstance();
     nestedArtboard->updateComponents();
     auto paints = nestedArtboard->shapePaints();
     REQUIRE(paints.size() == 1);
diff --git a/test/solo_test.cpp b/test/solo_test.cpp
index e53b250..30d57b0 100644
--- a/test/solo_test.cpp
+++ b/test/solo_test.cpp
@@ -237,9 +237,9 @@
     REQUIRE(artboard->is<rive::Artboard>());
     REQUIRE(artboard->find("Nested-Artboard-Active") != nullptr);
     auto nestedArtboardActive = artboard->find<rive::NestedArtboard>("Nested-Artboard-Active");
-    REQUIRE(nestedArtboardActive->artboard() != nullptr);
+    REQUIRE(nestedArtboardActive->artboardInstance() != nullptr);
 
-    auto nestedArtboardActiveArtboardInstance = nestedArtboardActive->artboard();
+    auto nestedArtboardActiveArtboardInstance = nestedArtboardActive->artboardInstance();
     auto activeRect =
         nestedArtboardActiveArtboardInstance->find<rive::Shape>("Clickable-Rectangle");
     REQUIRE(activeRect != nullptr);
@@ -250,8 +250,8 @@
 
     REQUIRE(artboard->find("Nested-Artboard-Inactive") != nullptr);
     auto nestedArtboardInactive = artboard->find<rive::NestedArtboard>("Nested-Artboard-Inactive");
-    REQUIRE(nestedArtboardInactive->artboard() != nullptr);
-    auto nestedArtboardInactiveArtboardInstance = nestedArtboardInactive->artboard();
+    REQUIRE(nestedArtboardInactive->artboardInstance() != nullptr);
+    auto nestedArtboardInactiveArtboardInstance = nestedArtboardInactive->artboardInstance();
     auto inactiveRect =
         nestedArtboardInactiveArtboardInstance->find<rive::Shape>("Clickable-Rectangle");
     REQUIRE(inactiveRect != nullptr);