diff --git a/common/app/common/attrs.cljc b/common/app/common/attrs.cljc
index 31fab1a22..d32311eea 100644
--- a/common/app/common/attrs.cljc
+++ b/common/app/common/attrs.cljc
@@ -9,71 +9,68 @@
 
 (ns app.common.attrs)
 
+;; Extract some attributes of a list of shapes.
+;; For each attribute, if the value is the same in all shapes,
+;; wll take this value. If there is any shape that is different,
+;; the value of the attribute will be the keyword :multiple.
+;;
+;; If some shape has the value nil in any attribute, it's
+;; considered a different value. If the shape does not contain
+;; the attribute, it's ignored in the final result.
+;;
+;; Example:
+;;   (def shapes [{:stroke-color "#ff0000"
+;;                 :stroke-width 3
+;;                 :fill-color "#0000ff"
+;;                 :x 1000 :y 2000 :rx nil}
+;;                {:stroke-width "#ff0000"
+;;                 :stroke-width 5
+;;                 :x 1500 :y 2000}])
+;;
+;;   (get-attrs-multi shapes [:stroke-color
+;;                            :stroke-width
+;;                            :fill-color
+;;                            :rx
+;;                            :ry])
+;;   >>> {:stroke-color "#ff0000"
+;;        :stroke-width :multiple
+;;        :fill-color "#0000ff"
+;;        :rx nil
+;;        :ry nil}
+;;
+
 (defn get-attrs-multi
-  ([shapes attrs] (get-attrs-multi shapes attrs = identity))
-  ([shapes attrs eq-fn sel-fn]
-   ;; Extract some attributes of a list of shapes.
-   ;; For each attribute, if the value is the same in all shapes,
-   ;; wll take this value. If there is any shape that is different,
-   ;; the value of the attribute will be the keyword :multiple.
-   ;;
-   ;; If some shape has the value nil in any attribute, it's
-   ;; considered a different value. If the shape does not contain
-   ;; the attribute, it's ignored in the final result.
-   ;;
-   ;; Example:
-   ;;   (def shapes [{:stroke-color "#ff0000"
-   ;;                 :stroke-width 3
-   ;;                 :fill-color "#0000ff"
-   ;;                 :x 1000 :y 2000 :rx nil}
-   ;;                {:stroke-width "#ff0000"
-   ;;                 :stroke-width 5
-   ;;                 :x 1500 :y 2000}])
-   ;;
-   ;;   (get-attrs-multi shapes [:stroke-color
-   ;;                            :stroke-width
-   ;;                            :fill-color
-   ;;                            :rx
-   ;;                            :ry])
-   ;;   >>> {:stroke-color "#ff0000"
-   ;;        :stroke-width :multiple
-   ;;        :fill-color "#0000ff"
-   ;;        :rx nil
-   ;;        :ry nil}
-   ;;
-   (let [defined-shapes (filter some? shapes)
+  ([objs attrs]
+   (get-attrs-multi objs attrs = identity))
 
-         combine-value (fn [v1 v2]
-                         (cond
-                           (and (= v1 :undefined) (= v2 :undefined)) :undefined
-                           (= v1 :undefined) (if (= v2 :multiple) :multiple (sel-fn v2))
-                           (= v2 :undefined) (if (= v1 :multiple) :multiple (sel-fn v1))
-                           (or (= v1 :multiple) (= v2 :multiple)) :multiple
-                           (eq-fn v1 v2) (sel-fn v1)
-                           :else :multiple))
+  ([objs attrs eqfn sel]
 
-         combine-values (fn [attrs shape values]
-                          (map #(combine-value (get shape % :undefined)
-                                               (get values % :undefined)) attrs))
+   (loop [attr (first attrs)
+          attrs (rest attrs)
+          result (transient {})]
 
-         select-attrs (fn [shape attrs]
-                        (zipmap attrs (map #(get shape % :undefined) attrs)))
+     (if attr
+       (let [value
+             (loop [curr (first objs)
+                    objs (rest objs)
+                    value ::undefined]
 
-         reducer (fn [result shape]
-                   (zipmap attrs (combine-values attrs shape result)))
+               (if (and curr (not= value :multiple))
+                 ;;
+                 (let [new-val (get curr attr ::undefined)
+                       value (cond
+                               (= new-val ::undefined) value
+                               (= value ::undefined)   (sel new-val)
+                               (eqfn new-val value)    value
+                               :else                   :multiple)]
+                   (recur (first objs) (rest objs) value))
+                 ;;
+                 value))]
+         (recur (first attrs)
+                (rest attrs)
+                (cond-> result
+                  (not= value ::undefined)
+                  (assoc! attr value))))
 
-         combined (reduce reducer
-                          (select-attrs (first defined-shapes) attrs)
-                          (rest defined-shapes))
+       (persistent! result)))))
 
-         cleanup-value (fn [value]
-                         (if (= value :undefined) nil value))
-
-         cleanup (fn [result]
-                   (->> attrs
-                        (map #(get result %))
-                        (zipmap attrs)
-                        (filter #(not= (second %) :undefined))
-                        (into {})))]
-
-     (cleanup combined))))
diff --git a/common/app/common/geom/shapes.cljc b/common/app/common/geom/shapes.cljc
index d40ab6114..b19a00bcb 100644
--- a/common/app/common/geom/shapes.cljc
+++ b/common/app/common/geom/shapes.cljc
@@ -124,9 +124,6 @@
   [shape {:keys [x y]}]
   (move shape (gpt/point (- x) (- y))))
 
-(defn translate-from-frame
-  [shape {:keys [x y]}]
-  (move shape (gpt/point x y)))
 
 ;; --- Helpers
 
diff --git a/frontend/src/app/main/ui/shapes/text.cljs b/frontend/src/app/main/ui/shapes/text.cljs
index 6124f4575..467903f3b 100644
--- a/frontend/src/app/main/ui/shapes/text.cljs
+++ b/frontend/src/app/main/ui/shapes/text.cljs
@@ -18,64 +18,89 @@
    [app.util.object :as obj]
    [app.util.color :as uc]
    [app.main.ui.shapes.text.styles :as sts]
-   [app.main.ui.shapes.text.embed :as ste]))
+   [app.main.ui.shapes.text.embed :as ste]
+   [app.util.perf :as perf]))
+
+(mf/defc render-text
+  {::mf/wrap-props false}
+  [props]
+  (let [node (obj/get props "node")
+        text (:text node)
+        style (sts/generate-text-styles props)]
+    [:span {:style style
+            :className (when (:fill-color-gradient node) "gradient")}
+     (if (= text "") "\u00A0" text)]))
+
+(mf/defc render-root
+  {::mf/wrap-props false}
+  [props]
+  (let [node (obj/get props "node")
+        embed-fonts? (obj/get props "embed-fonts?")
+        children (obj/get props "children")
+        style (sts/generate-root-styles props)]
+    [:div.root.rich-text
+     {:style style
+      :xmlns "http://www.w3.org/1999/xhtml"}
+     [:*
+      [:style ".gradient { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
+      (when embed-fonts?
+        [ste/embed-fontfaces-style {:node node}])]
+     children]))
+
+(mf/defc render-paragraph-set
+  {::mf/wrap-props false}
+  [props]
+  (let [node (obj/get props "node")
+        children (obj/get props "children")
+        style (sts/generate-paragraph-set-styles props)]
+    [:div.paragraph-set {:style style} children]))
+
+(mf/defc render-paragraph
+  {::mf/wrap-props false}
+  [props]
+  (let [node (obj/get props "node")
+        children (obj/get props "children")
+        style (sts/generate-paragraph-styles props)]
+    [:p.paragraph {:style style} children]))
 
 ;; -- Text nodes
-(mf/defc text-node
-  [{:keys [node index shape] :as props}]
-  (let [embed-resources? (mf/use-ctx muc/embed-ctx)
-        {:keys [type text children]} node
-        props #js {:shape shape}
-        render-node
-        (fn [index node]
-          (mf/element text-node {:index index
-                                 :node node
-                                 :key index
-                                 :shape shape}))]
-
+(mf/defc render-node
+  {::mf/wrap-props false}
+  [props]
+  (let [node (obj/get props "node")
+        index (obj/get props "index")
+        {:keys [type text children]} node]
     (if (string? text)
-      (let [style (sts/generate-text-styles (clj->js node) props)]
-        [:span {:style style
-                :className (when (:fill-color-gradient node) "gradient")}
-         (if (= text "") "\u00A0" text)])
+      [:> render-text props]
 
-      (let [children (map-indexed render-node children)]
-        (case type
-          "root"
-          (let [style (sts/generate-root-styles (clj->js node) props)]
-            [:div.root.rich-text
-             {:key index
-              :style style
-              :xmlns "http://www.w3.org/1999/xhtml"}
-             [:*
-              [:style ".gradient { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
-              (when embed-resources?
-                [ste/embed-fontfaces-style {:node node}])]
-             children])
-
-          "paragraph-set"
-          (let [style (sts/generate-paragraph-set-styles (clj->js node) props)]
-            [:div.paragraph-set {:key index :style style} children])
-
-          "paragraph"
-          (let [style (sts/generate-paragraph-styles (clj->js node) props)]
-            [:p.paragraph {:key index :style style} children])
-
-          nil)))))
+      (let [component (case type
+                        "root" render-root
+                        "paragraph-set" render-paragraph-set
+                        "paragraph" render-paragraph
+                        nil)]
+        (when component
+          [:> component (obj/set! props "key" index)
+           (for [[index child] (d/enumerate children)]
+             (let [props (-> props
+                             (obj/set! "node" child)
+                             (obj/set! "index" index))]
+               [:> render-node props]))])))))
 
 (mf/defc text-content
-  {::mf/wrap-props false
-   ::mf/wrap [mf/memo]}
+  {::mf/wrap-props false}
   [props]
   (let [root (obj/get props "content")
-        shape (obj/get props "shape")]
-    [:& text-node {:index 0
-                   :node root
-                   :shape shape}]))
+        shape (obj/get props "shape")
+        embed-fonts? (obj/get props "embed-fonts?")]
+    [:& render-node {:index 0
+                     :node root
+                     :shape shape
+                     :embed-fonts? embed-fonts?}]))
 
 (defn- retrieve-colors
   [shape]
-  (let [colors (->> shape :content
+  (let [colors (->> shape
+                    :content
                     (tree-seq map? :children)
                     (into #{} (comp (map :fill-color) (filter string?))))]
     (if (empty? colors)
@@ -87,8 +112,8 @@
    ::mf/forward-ref true}
   [props ref]
   (let [shape     (unchecked-get props "shape")
-        selected? (unchecked-get props "selected?")
         grow-type (unchecked-get props "grow-type")
+        embed-fonts? (mf/use-ctx muc/embed-ctx)
         {:keys [id x y width height content]} shape]
     [:foreignObject {:x x
                      :y y
@@ -99,4 +124,5 @@
                      :height (if (#{:auto-height :auto-width} grow-type) 10000 height)
                      :ref ref}
      [:& text-content {:shape shape
-                       :content (:content shape)}]]))
+                       :content (:content shape)
+                       :embed-fonts? embed-fonts?}]]))
diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs
index 0ace63739..c859d2c0c 100644
--- a/frontend/src/app/main/ui/shapes/text/styles.cljs
+++ b/frontend/src/app/main/ui/shapes/text/styles.cljs
@@ -17,111 +17,115 @@
    [app.util.text :as ut]))
 
 (defn generate-root-styles
-  [data props]
-  (let [valign (obj/get data "vertical-align" "top")
-        talign (obj/get data "text-align" "flex-start")
-        shape  (obj/get props "shape")
-        base   #js {:height (or (:height shape) "100%")
-                    :width (or (:width shape) "100%")
-                    :display "flex"}]
-    (cond-> base
-      (= valign "top")     (obj/set! "alignItems" "flex-start")
-      (= valign "center")  (obj/set! "alignItems" "center")
-      (= valign "bottom")  (obj/set! "alignItems" "flex-end")
+  ([props] (generate-root-styles (clj->js (obj/get props "node")) props))
+  ([data props]
+   (let [valign (obj/get data "vertical-align" "top")
+         talign (obj/get data "text-align" "flex-start")
+         shape  (obj/get props "shape")
+         base   #js {:height (or (:height shape) "100%")
+                     :width (or (:width shape) "100%")
+                     :display "flex"}]
+     (cond-> base
+       (= valign "top")     (obj/set! "alignItems" "flex-start")
+       (= valign "center")  (obj/set! "alignItems" "center")
+       (= valign "bottom")  (obj/set! "alignItems" "flex-end")
 
-      (= talign "left")    (obj/set! "justifyContent" "flex-start")
-      (= talign "center")  (obj/set! "justifyContent" "center")
-      (= talign "right")   (obj/set! "justifyContent" "flex-end")
-      (= talign "justify") (obj/set! "justifyContent" "stretch"))))
+       (= talign "left")    (obj/set! "justifyContent" "flex-start")
+       (= talign "center")  (obj/set! "justifyContent" "center")
+       (= talign "right")   (obj/set! "justifyContent" "flex-end")
+       (= talign "justify") (obj/set! "justifyContent" "stretch")))))
 
 (defn generate-paragraph-set-styles
-  [data props]
-  ;; The position absolute is used so the paragraph is "outside"
-  ;; the normal layout and can grow outside its parent
-  ;; We use this element to measure the size of the text
-  (let [base #js {:display "inline-block"}]
-    base))
+  ([props] (generate-paragraph-set-styles nil props))
+  ([data props]
+   ;; The position absolute is used so the paragraph is "outside"
+   ;; the normal layout and can grow outside its parent
+   ;; We use this element to measure the size of the text
+   (let [base #js {:display "inline-block"}]
+     base)))
 
 (defn generate-paragraph-styles
-  [data props]
-  (let [shape  (obj/get props "shape")
-        grow-type (:grow-type shape)
-        base #js {:fontSize "14px"
-                  :margin "inherit"
-                  :lineHeight "1.2"}
-        lh (obj/get data "line-height")
-        ta (obj/get data "text-align")]
-    (cond-> base
-      ta (obj/set! "textAlign" ta)
-      lh (obj/set! "lineHeight" lh)
-      (= grow-type :auto-width) (obj/set! "whiteSpace" "pre"))))
+  ([props] (generate-paragraph-styles (clj->js (obj/get props "node")) props))
+  ([data props]
+   (let [shape  (obj/get props "shape")
+         grow-type (:grow-type shape)
+         base #js {:fontSize "14px"
+                   :margin "inherit"
+                   :lineHeight "1.2"}
+         lh (obj/get data "line-height")
+         ta (obj/get data "text-align")]
+     (cond-> base
+       ta (obj/set! "textAlign" ta)
+       lh (obj/set! "lineHeight" lh)
+       (= grow-type :auto-width) (obj/set! "whiteSpace" "pre")))))
 
 (defn generate-text-styles
-  [data props]
-  (let [letter-spacing (obj/get data "letter-spacing")
-        text-decoration (obj/get data "text-decoration")
-        text-transform (obj/get data "text-transform")
-        line-height (obj/get data "line-height")
+  ([props] (generate-text-styles (clj->js (obj/get props "node")) props))
+  ([data props]
+   (let [letter-spacing (obj/get data "letter-spacing")
+         text-decoration (obj/get data "text-decoration")
+         text-transform (obj/get data "text-transform")
+         line-height (obj/get data "line-height")
 
-        font-id (obj/get data "font-id" (:font-id ut/default-text-attrs))
-        font-variant-id (obj/get data "font-variant-id")
+         font-id (obj/get data "font-id" (:font-id ut/default-text-attrs))
+         font-variant-id (obj/get data "font-variant-id")
 
-        font-family (obj/get data "font-family")
-        font-size  (obj/get data "font-size")
+         font-family (obj/get data "font-family")
+         font-size  (obj/get data "font-size")
 
-        ;; Old properties for backwards compatibility
-        fill (obj/get data "fill")
-        opacity (obj/get data "opacity" 1)
+         ;; Old properties for backwards compatibility
+         fill (obj/get data "fill")
+         opacity (obj/get data "opacity" 1)
 
-        fill-color (obj/get data "fill-color" fill)
-        fill-opacity (obj/get data "fill-opacity" opacity)
-        fill-color-gradient (obj/get data "fill-color-gradient" nil)
-        fill-color-gradient (when fill-color-gradient
-                              (-> (js->clj fill-color-gradient :keywordize-keys true)
-                                  (update :type keyword)))
+         fill-color (obj/get data "fill-color" fill)
+         fill-opacity (obj/get data "fill-opacity" opacity)
+         fill-color-gradient (obj/get data "fill-color-gradient" nil)
+         fill-color-gradient (when fill-color-gradient
+                               (-> (js->clj fill-color-gradient :keywordize-keys true)
+                                   (update :type keyword)))
 
-        ;; Uncomment this to allow to remove text colors. This could break the texts that already exist
-        ;;[r g b a] (if (nil? fill-color)
-        ;;            [0 0 0 0] ;; Transparent color
-        ;;            (uc/hex->rgba fill-color fill-opacity))
+         ;; Uncomment this to allow to remove text colors. This could break the texts that already exist
+         ;;[r g b a] (if (nil? fill-color)
+         ;;            [0 0 0 0] ;; Transparent color
+         ;;            (uc/hex->rgba fill-color fill-opacity))
 
-        [r g b a] (uc/hex->rgba fill-color fill-opacity)
+         [r g b a] (uc/hex->rgba fill-color fill-opacity)
 
-        text-color (if fill-color-gradient
-                     (uc/gradient->css (js->clj fill-color-gradient))
-                     (str/format "rgba(%s, %s, %s, %s)" r g b a))
+         text-color (if fill-color-gradient
+                      (uc/gradient->css (js->clj fill-color-gradient))
+                      (str/format "rgba(%s, %s, %s, %s)" r g b a))
 
-        fontsdb (deref fonts/fontsdb)
+         fontsdb (deref fonts/fontsdb)
 
-        base #js {:textDecoration text-decoration
-                  :textTransform text-transform
-                  :lineHeight (or line-height "inherit")
-                  :color text-color
-                  "--text-color" text-color}]
+         base #js {:textDecoration text-decoration
+                   :textTransform text-transform
+                   :lineHeight (or line-height "inherit")
+                   :color text-color
+                   "--text-color" text-color}]
 
-    (when (and (string? letter-spacing)
-               (pos? (alength letter-spacing)))
-      (obj/set! base "letterSpacing" (str letter-spacing "px")))
+     (when (and (string? letter-spacing)
+                (pos? (alength letter-spacing)))
+       (obj/set! base "letterSpacing" (str letter-spacing "px")))
 
-    (when (and (string? font-size)
-               (pos? (alength font-size)))
-      (obj/set! base "fontSize" (str font-size "px")))
+     (when (and (string? font-size)
+                (pos? (alength font-size)))
+       (obj/set! base "fontSize" (str font-size "px")))
 
-    (when (and (string? font-id)
-               (pos? (alength font-id)))
-      (fonts/ensure-loaded! font-id)
-      (let [font (get fontsdb font-id)]
-        (let [font-family (or (:family font)
-                              (obj/get data "fontFamily"))
-              font-variant (d/seek #(= font-variant-id (:id %))
-                                   (:variants font))
-              font-style  (or (:style font-variant)
-                              (obj/get data "fontStyle"))
-              font-weight (or (:weight font-variant)
-                              (obj/get data "fontWeight"))]
-          (obj/set! base "fontFamily" font-family)
-          (obj/set! base "fontStyle" font-style)
-          (obj/set! base "fontWeight" font-weight))))
+     (when (and (string? font-id)
+                (pos? (alength font-id)))
+       (fonts/ensure-loaded! font-id)
+       (let [font (get fontsdb font-id)]
+         (let [font-family (or (:family font)
+                               (obj/get data "fontFamily"))
+               font-variant (d/seek #(= font-variant-id (:id %))
+                                    (:variants font))
+               font-style  (or (:style font-variant)
+                               (obj/get data "fontStyle"))
+               font-weight (or (:weight font-variant)
+                               (obj/get data "fontWeight"))]
+           (obj/set! base "fontFamily" font-family)
+           (obj/set! base "fontStyle" font-style)
+           (obj/set! base "fontWeight" font-weight))))
 
 
-    base))
+     base)))
diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs
index 40f1ba745..212979b81 100644
--- a/frontend/src/app/main/ui/workspace/shapes/text.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs
@@ -34,46 +34,33 @@
 
 ;; --- Events
 
-(defn use-double-click [{:keys [id]} selected?]
+(defn use-double-click [{:keys [id]}]
   (mf/use-callback
-   (mf/deps id selected?)
+   (mf/deps id)
    (fn [event]
      (dom/stop-propagation event)
      (dom/prevent-default event)
-     (when selected?
-       (st/emit! (dw/start-edition-mode id))))))
+     (st/emit! (dw/start-edition-mode id)))))
 
 ;; --- Text Wrapper for workspace
 
-(mf/defc text-wrapper
+(mf/defc text-static-content
+  [{:keys [shape]}]
+  [:& text/text-shape {:shape shape
+                       :grow-type (:grow-type shape)}])
+
+(mf/defc text-resize-content
   {::mf/wrap-props false}
   [props]
-  (let [{:keys [id name x y width height grow-type] :as shape} (unchecked-get props "shape")
-        ghost? (mf/use-ctx muc/ghost-ctx)
-        selected-iref (mf/use-memo (mf/deps (:id shape))
-                                   #(refs/make-selected-ref (:id shape)))
-        selected? (mf/deref selected-iref)
-        edition   (mf/deref refs/selected-edition)
-        current-transform (mf/deref refs/current-transform)
-
-        render-editor (mf/use-state false)
-
-        edition?  (= edition id)
-        embed-resources? (mf/use-ctx muc/embed-ctx)
-
-        handle-mouse-down (we/use-mouse-down shape)
-        handle-context-menu (we/use-context-menu shape)
-        handle-pointer-enter (we/use-pointer-enter shape)
-        handle-pointer-leave (we/use-pointer-leave shape)
-        handle-double-click (use-double-click shape selected?)
-
+  (let [shape (obj/get props "shape")
+        {:keys [id name x y grow-type]} shape
         paragraph-ref (mf/use-state nil)
 
         handle-resize-text
         (mf/use-callback
          (mf/deps id)
          (fn [entries]
-           (when (and (not ghost?) (seq entries))
+           (when (seq entries)
              ;; RequestAnimationFrame so the "loop limit error" error is not thrown
              ;; https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
              (timers/raf
@@ -97,24 +84,41 @@
     (mf/use-effect
      (mf/deps @paragraph-ref handle-resize-text grow-type)
      (fn []
-       (when (not ghost?)
-         (when-let [paragraph-node @paragraph-ref]
-           (let [observer (js/ResizeObserver. handle-resize-text)]
-             (log/debug :msg "Attach resize observer" :shape-id id :shape-name name)
-             (.observe observer paragraph-node)
-             #(.disconnect observer))))))
+       (when-let [paragraph-node @paragraph-ref]
+         (let [observer (js/ResizeObserver. handle-resize-text)]
+           (log/debug :msg "Attach resize observer" :shape-id id :shape-name name)
+           (.observe observer paragraph-node)
+           #(.disconnect observer)))))
 
+    [:& text/text-shape {:ref text-ref-cb
+                         :shape shape
+                         :grow-type (:grow-type shape)}]))
+
+(mf/defc text-wrapper
+  {::mf/wrap-props false}
+  [props]
+  (let [{:keys [id x y width height] :as shape} (unchecked-get props "shape")
+        ghost?   (mf/use-ctx muc/ghost-ctx)
+        edition  (mf/deref refs/selected-edition)
+        edition? (= edition id)
+
+        handle-mouse-down (we/use-mouse-down shape)
+        handle-context-menu (we/use-context-menu shape)
+        handle-pointer-enter (we/use-pointer-enter shape)
+        handle-pointer-leave (we/use-pointer-leave shape)
+        handle-double-click (use-double-click shape)]
 
     [:> shape-container {:shape shape}
      ;; We keep hidden the shape when we're editing so it keeps track of the size
      ;; and updates the selrect acordingly
      [:g.text-shape {:opacity (when edition? 0)
                      :pointer-events "none"}
-      [:& text/text-shape {:key (str "text-shape" (:id shape))
-                           :ref text-ref-cb
-                           :shape shape
-                           :selected? selected?
-                           :grow-type (:grow-type shape)}]]
+
+      (if ghost?
+        [:& text-static-content {:shape shape}]
+        [:& text-resize-content {:shape shape}])]
+
+
      (when (and (not ghost?) edition?)
        [:& editor/text-shape-edit {:key (str "editor" (:id shape))
                                    :shape shape}])
diff --git a/frontend/src/app/util/text.cljs b/frontend/src/app/util/text.cljs
index 607e9251c..ef75f6535 100644
--- a/frontend/src/app/util/text.cljs
+++ b/frontend/src/app/util/text.cljs
@@ -93,12 +93,31 @@
     (rec-fn {} node)))
 
 
+(defn content->nodes [node]
+  (loop [result (transient [])
+         curr node
+         pending (transient [])]
+
+    (let [result (conj! result curr)]
+      ;; Adds children to the pending list
+      (let [children (:children curr)
+            pending (loop [child (first children)
+                           children (rest children)
+                           pending pending]
+                      (if child
+                        (recur (first children)
+                               (rest children)
+                               (conj! pending child))
+                        pending))]
+
+        (if (= 0 (count pending))
+          (persistent! result)
+          ;; Iterates with the next value in pending
+          (let [next (get pending (dec (count pending)))]
+            (recur result next (pop! pending))))))))
+
 (defn get-text-attrs-multi
   [node attrs]
-  (let [rec-fn
-        (fn rec-fn [current node]
-          (let [current (reduce rec-fn current (:children node []))]
-            (get-attrs-multi [current node] attrs)))]
-    (merge (select-keys default-text-attrs attrs)
-           (rec-fn {} node))))
+  (let [nodes (content->nodes node)]
+    (get-attrs-multi nodes attrs)))