diff --git a/src/actions/actionBoundText.tsx b/src/actions/actionBoundText.tsx
index 7849730d..c3ebc920 100644
--- a/src/actions/actionBoundText.tsx
+++ b/src/actions/actionBoundText.tsx
@@ -1,7 +1,13 @@
-import { VERTICAL_ALIGN } from "../constants";
-import { getNonDeletedElements, isTextElement } from "../element";
+import {
+  BOUND_TEXT_PADDING,
+  ROUNDNESS,
+  TEXT_ALIGN,
+  VERTICAL_ALIGN,
+} from "../constants";
+import { getNonDeletedElements, isTextElement, newElement } from "../element";
 import { mutateElement } from "../element/mutateElement";
 import {
+  computeContainerDimensionForBoundText,
   getBoundTextElement,
   measureText,
   redrawTextBoundingBox,
@@ -13,8 +19,11 @@ import {
 import {
   hasBoundTextElement,
   isTextBindableContainer,
+  isUsingAdaptiveRadius,
 } from "../element/typeChecks";
 import {
+  ExcalidrawElement,
+  ExcalidrawLinearElement,
   ExcalidrawTextContainer,
   ExcalidrawTextElement,
 } from "../element/types";
@@ -129,19 +138,152 @@ export const actionBindText = register({
       }),
     });
     redrawTextBoundingBox(textElement, container);
-    const updatedElements = elements.slice();
-    const textElementIndex = updatedElements.findIndex(
-      (ele) => ele.id === textElement.id,
-    );
-    updatedElements.splice(textElementIndex, 1);
-    const containerIndex = updatedElements.findIndex(
-      (ele) => ele.id === container.id,
-    );
-    updatedElements.splice(containerIndex + 1, 0, textElement);
+
     return {
-      elements: updatedElements,
+      elements: pushTextAboveContainer(elements, container, textElement),
       appState: { ...appState, selectedElementIds: { [container.id]: true } },
       commitToHistory: true,
     };
   },
 });
+
+const pushTextAboveContainer = (
+  elements: readonly ExcalidrawElement[],
+  container: ExcalidrawElement,
+  textElement: ExcalidrawTextElement,
+) => {
+  const updatedElements = elements.slice();
+  const textElementIndex = updatedElements.findIndex(
+    (ele) => ele.id === textElement.id,
+  );
+  updatedElements.splice(textElementIndex, 1);
+
+  const containerIndex = updatedElements.findIndex(
+    (ele) => ele.id === container.id,
+  );
+  updatedElements.splice(containerIndex + 1, 0, textElement);
+  return updatedElements;
+};
+
+const pushContainerBelowText = (
+  elements: readonly ExcalidrawElement[],
+  container: ExcalidrawElement,
+  textElement: ExcalidrawTextElement,
+) => {
+  const updatedElements = elements.slice();
+  const containerIndex = updatedElements.findIndex(
+    (ele) => ele.id === container.id,
+  );
+  updatedElements.splice(containerIndex, 1);
+
+  const textElementIndex = updatedElements.findIndex(
+    (ele) => ele.id === textElement.id,
+  );
+  updatedElements.splice(textElementIndex, 0, container);
+  return updatedElements;
+};
+
+export const actionCreateContainerFromText = register({
+  name: "createContainerFromText",
+  contextItemLabel: "labels.createContainerFromText",
+  trackEvent: { category: "element" },
+  predicate: (elements, appState) => {
+    const selectedElements = getSelectedElements(elements, appState);
+    return selectedElements.length === 1 && isTextElement(selectedElements[0]);
+  },
+  perform: (elements, appState) => {
+    const selectedElements = getSelectedElements(
+      getNonDeletedElements(elements),
+      appState,
+    );
+    const updatedElements = elements.slice();
+    if (selectedElements.length === 1 && isTextElement(selectedElements[0])) {
+      const textElement = selectedElements[0];
+      const container = newElement({
+        type: "rectangle",
+        backgroundColor: appState.currentItemBackgroundColor,
+        boundElements: [
+          ...(textElement.boundElements || []),
+          { id: textElement.id, type: "text" },
+        ],
+        angle: textElement.angle,
+        fillStyle: appState.currentItemFillStyle,
+        strokeColor: appState.currentItemStrokeColor,
+        roughness: appState.currentItemRoughness,
+        strokeWidth: appState.currentItemStrokeWidth,
+        strokeStyle: appState.currentItemStrokeStyle,
+        roundness:
+          appState.currentItemRoundness === "round"
+            ? {
+                type: isUsingAdaptiveRadius("rectangle")
+                  ? ROUNDNESS.ADAPTIVE_RADIUS
+                  : ROUNDNESS.PROPORTIONAL_RADIUS,
+              }
+            : null,
+        opacity: 100,
+        locked: false,
+        x: textElement.x - BOUND_TEXT_PADDING,
+        y: textElement.y - BOUND_TEXT_PADDING,
+        width: computeContainerDimensionForBoundText(
+          textElement.width,
+          "rectangle",
+        ),
+        height: computeContainerDimensionForBoundText(
+          textElement.height,
+          "rectangle",
+        ),
+        groupIds: textElement.groupIds,
+      });
+
+      // update bindings
+      if (textElement.boundElements?.length) {
+        const linearElementIds = textElement.boundElements
+          .filter((ele) => ele.type === "arrow")
+          .map((el) => el.id);
+        const linearElements = updatedElements.filter((ele) =>
+          linearElementIds.includes(ele.id),
+        ) as ExcalidrawLinearElement[];
+        linearElements.forEach((ele) => {
+          let startBinding = null;
+          let endBinding = null;
+          if (ele.startBinding) {
+            startBinding = { ...ele.startBinding, elementId: container.id };
+          }
+          if (ele.endBinding) {
+            endBinding = { ...ele.endBinding, elementId: container.id };
+          }
+          mutateElement(ele, { startBinding, endBinding });
+        });
+      }
+
+      mutateElement(textElement, {
+        containerId: container.id,
+        verticalAlign: VERTICAL_ALIGN.MIDDLE,
+        textAlign: TEXT_ALIGN.CENTER,
+        boundElements: null,
+      });
+      redrawTextBoundingBox(textElement, container);
+
+      return {
+        elements: pushContainerBelowText(
+          [...elements, container],
+          container,
+          textElement,
+        ),
+        appState: {
+          ...appState,
+          selectedElementIds: {
+            [container.id]: true,
+            [textElement.id]: false,
+          },
+        },
+        commitToHistory: true,
+      };
+    }
+    return {
+      elements: updatedElements,
+      appState,
+      commitToHistory: true,
+    };
+  },
+});
diff --git a/src/actions/types.ts b/src/actions/types.ts
index 54bd5a26..baa37eaa 100644
--- a/src/actions/types.ts
+++ b/src/actions/types.ts
@@ -113,7 +113,8 @@ export type ActionName =
   | "toggleLock"
   | "toggleLinearEditor"
   | "toggleEraserTool"
-  | "toggleHandTool";
+  | "toggleHandTool"
+  | "createContainerFromText";
 
 export type PanelComponentProps = {
   elements: readonly ExcalidrawElement[];
diff --git a/src/components/App.tsx b/src/components/App.tsx
index 24da7d85..a8240f12 100644
--- a/src/components/App.tsx
+++ b/src/components/App.tsx
@@ -284,6 +284,7 @@ import { actionPaste } from "../actions/actionClipboard";
 import { actionToggleHandTool } from "../actions/actionCanvas";
 import { jotaiStore } from "../jotai";
 import { activeConfirmDialogAtom } from "./ActiveConfirmDialog";
+import { actionCreateContainerFromText } from "../actions/actionBoundText";
 
 const deviceContextInitialValue = {
   isSmScreen: false,
@@ -6237,6 +6238,7 @@ class App extends React.Component<AppProps, AppState> {
       actionGroup,
       actionUnbindText,
       actionBindText,
+      actionCreateContainerFromText,
       actionUngroup,
       CONTEXT_MENU_SEPARATOR,
       actionAddToLibrary,
diff --git a/src/element/textElement.test.ts b/src/element/textElement.test.ts
index 87219b29..7bc361b4 100644
--- a/src/element/textElement.test.ts
+++ b/src/element/textElement.test.ts
@@ -1,7 +1,7 @@
 import { BOUND_TEXT_PADDING } from "../constants";
 import { API } from "../tests/helpers/api";
 import {
-  computeContainerHeightForBoundText,
+  computeContainerDimensionForBoundText,
   getContainerCoords,
   getMaxContainerWidth,
   getMaxContainerHeight,
@@ -35,10 +35,11 @@ describe("Test wrapText", () => {
 
   describe("When text doesn't contain new lines", () => {
     const text = "Hello whats up";
+
     [
       {
         desc: "break all words when width of each word is less than container width",
-        width: 90,
+        width: 80,
         res: `Hello 
 whats 
 up`,
@@ -62,7 +63,7 @@ p`,
       {
         desc: "break words as per the width",
 
-        width: 150,
+        width: 140,
         res: `Hello whats 
 up`,
       },
@@ -93,7 +94,7 @@ whats up`;
     [
       {
         desc: "break all words when width of each word is less than container width",
-        width: 90,
+        width: 80,
         res: `Hello
 whats 
 up`,
@@ -214,7 +215,7 @@ describe("Test measureText", () => {
     });
   });
 
-  describe("Test computeContainerHeightForBoundText", () => {
+  describe("Test computeContainerDimensionForBoundText", () => {
     const params = {
       width: 178,
       height: 194,
@@ -225,7 +226,9 @@ describe("Test measureText", () => {
         type: "rectangle",
         ...params,
       });
-      expect(computeContainerHeightForBoundText(element, 150)).toEqual(160);
+      expect(computeContainerDimensionForBoundText(150, element.type)).toEqual(
+        160,
+      );
     });
 
     it("should compute container height correctly for ellipse", () => {
@@ -233,7 +236,9 @@ describe("Test measureText", () => {
         type: "ellipse",
         ...params,
       });
-      expect(computeContainerHeightForBoundText(element, 150)).toEqual(226);
+      expect(computeContainerDimensionForBoundText(150, element.type)).toEqual(
+        226,
+      );
     });
 
     it("should compute container height correctly for diamond", () => {
@@ -241,7 +246,9 @@ describe("Test measureText", () => {
         type: "diamond",
         ...params,
       });
-      expect(computeContainerHeightForBoundText(element, 150)).toEqual(320);
+      expect(computeContainerDimensionForBoundText(150, element.type)).toEqual(
+        320,
+      );
     });
   });
 
diff --git a/src/element/textElement.ts b/src/element/textElement.ts
index 4d9fa5eb..a4c49479 100644
--- a/src/element/textElement.ts
+++ b/src/element/textElement.ts
@@ -12,11 +12,7 @@ import { BOUND_TEXT_PADDING, TEXT_ALIGN, VERTICAL_ALIGN } from "../constants";
 import { MaybeTransformHandleType } from "./transformHandles";
 import Scene from "../scene/Scene";
 import { isTextElement } from ".";
-import {
-  isBoundToContainer,
-  isImageElement,
-  isArrowElement,
-} from "./typeChecks";
+import { isBoundToContainer, isArrowElement } from "./typeChecks";
 import { LinearElementEditor } from "./linearElementEditor";
 import { AppState } from "../types";
 import { isTextBindableContainer } from "./typeChecks";
@@ -84,9 +80,9 @@ export const redrawTextBoundingBox = (
 
       let nextHeight = containerDims.height;
       if (metrics.height > maxContainerHeight) {
-        nextHeight = computeContainerHeightForBoundText(
-          container,
+        nextHeight = computeContainerDimensionForBoundText(
           metrics.height,
+          container.type,
         );
         mutateElement(container, { height: nextHeight });
         maxContainerHeight = getMaxContainerHeight(container);
@@ -188,9 +184,9 @@ export const handleBindTextResize = (
     }
     // increase height in case text element height exceeds
     if (nextHeight > maxHeight) {
-      containerHeight = computeContainerHeightForBoundText(
-        container,
+      containerHeight = computeContainerDimensionForBoundText(
         nextHeight,
+        container.type,
       );
 
       const diff = containerHeight - containerDims.height;
@@ -324,7 +320,6 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
   const lines: Array<string> = [];
   const originalLines = text.split("\n");
   const spaceWidth = getLineWidth(" ", font);
-
   const push = (str: string) => {
     if (str.trim()) {
       lines.push(str);
@@ -398,7 +393,7 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
           const word = words[index];
           currentLineWidthTillNow = getLineWidth(currentLine + word, font);
 
-          if (currentLineWidthTillNow >= maxWidth) {
+          if (currentLineWidthTillNow > maxWidth) {
             push(currentLine);
             currentLineWidthTillNow = 0;
             currentLine = "";
@@ -714,32 +709,34 @@ export const getTextBindableContainerAtPosition = (
   return isTextBindableContainer(hitElement, false) ? hitElement : null;
 };
 
-export const isValidTextContainer = (element: ExcalidrawElement) => {
-  return (
-    element.type === "rectangle" ||
-    element.type === "ellipse" ||
-    element.type === "diamond" ||
-    isImageElement(element) ||
-    isArrowElement(element)
-  );
-};
+const VALID_CONTAINER_TYPES = new Set([
+  "rectangle",
+  "ellipse",
+  "diamond",
+  "image",
+  "arrow",
+]);
 
-export const computeContainerHeightForBoundText = (
-  container: NonDeletedExcalidrawElement,
-  boundTextElementHeight: number,
+export const isValidTextContainer = (element: ExcalidrawElement) =>
+  VALID_CONTAINER_TYPES.has(element.type);
+
+export const computeContainerDimensionForBoundText = (
+  dimension: number,
+  containerType: ExtractSetType<typeof VALID_CONTAINER_TYPES>,
 ) => {
-  if (container.type === "ellipse") {
-    return Math.round(
-      ((boundTextElementHeight + BOUND_TEXT_PADDING * 2) / Math.sqrt(2)) * 2,
-    );
+  dimension = Math.ceil(dimension);
+  const padding = BOUND_TEXT_PADDING * 2;
+
+  if (containerType === "ellipse") {
+    return Math.round(((dimension + padding) / Math.sqrt(2)) * 2);
   }
-  if (isArrowElement(container)) {
-    return boundTextElementHeight + BOUND_TEXT_PADDING * 8 * 2;
+  if (containerType === "arrow") {
+    return dimension + padding * 8;
   }
-  if (container.type === "diamond") {
-    return 2 * (boundTextElementHeight + BOUND_TEXT_PADDING * 2);
+  if (containerType === "diamond") {
+    return 2 * (dimension + padding);
   }
-  return boundTextElementHeight + BOUND_TEXT_PADDING * 2;
+  return dimension + padding;
 };
 
 export const getMaxContainerWidth = (container: ExcalidrawElement) => {
diff --git a/src/element/textWysiwyg.test.tsx b/src/element/textWysiwyg.test.tsx
index 7de71198..48138ea0 100644
--- a/src/element/textWysiwyg.test.tsx
+++ b/src/element/textWysiwyg.test.tsx
@@ -19,6 +19,7 @@ import { API } from "../tests/helpers/api";
 import { mutateElement } from "./mutateElement";
 import { resize } from "../tests/utils";
 import { getOriginalContainerHeightFromCache } from "./textWysiwyg";
+
 // Unmount ReactDOM from root
 ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
 
@@ -1307,5 +1308,78 @@ describe("textWysiwyg", () => {
         `);
       });
     });
+
+    it("should wrap text in a container when wrap text in container triggered from context menu", async () => {
+      UI.clickTool("text");
+      mouse.clickAt(20, 30);
+      const editor = document.querySelector(
+        ".excalidraw-textEditorContainer > textarea",
+      ) as HTMLTextAreaElement;
+
+      fireEvent.change(editor, {
+        target: {
+          value: "Excalidraw is an opensource virtual collaborative whiteboard",
+        },
+      });
+
+      editor.dispatchEvent(new Event("input"));
+      await new Promise((cb) => setTimeout(cb, 0));
+      editor.blur();
+      expect(h.elements[1].width).toBe(600);
+      expect(h.elements[1].height).toBe(24);
+      expect((h.elements[1] as ExcalidrawTextElement).text).toBe(
+        "Excalidraw is an opensource virtual collaborative whiteboard",
+      );
+
+      API.setSelectedElements([h.elements[1]]);
+
+      fireEvent.contextMenu(GlobalTestState.canvas, {
+        button: 2,
+        clientX: 20,
+        clientY: 30,
+      });
+
+      const contextMenu = document.querySelector(".context-menu");
+      fireEvent.click(
+        queryByText(contextMenu as HTMLElement, "Wrap text in a container")!,
+      );
+      expect(h.elements.length).toBe(3);
+
+      expect(h.elements[1]).toEqual(
+        expect.objectContaining({
+          angle: 0,
+          backgroundColor: "transparent",
+          boundElements: [
+            {
+              id: h.elements[2].id,
+              type: "text",
+            },
+          ],
+          fillStyle: "hachure",
+          groupIds: [],
+          height: 34,
+          isDeleted: false,
+          link: null,
+          locked: false,
+          opacity: 100,
+          roughness: 1,
+          roundness: {
+            type: 3,
+          },
+          strokeColor: "#000000",
+          strokeStyle: "solid",
+          strokeWidth: 1,
+          type: "rectangle",
+          updated: 1,
+          version: 1,
+          width: 610,
+          x: 15,
+          y: 25,
+        }),
+      );
+      expect((h.elements[2] as ExcalidrawTextElement).text).toBe(
+        "Excalidraw is an opensource virtual collaborative whiteboard",
+      );
+    });
   });
 });
diff --git a/src/global.d.ts b/src/global.d.ts
index df7eeb37..4ccd8f3f 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -165,3 +165,5 @@ declare module "image-blob-reduce" {
   const reduce: ImageBlobReduce.ImageBlobReduceStatic;
   export = reduce;
 }
+
+type ExtractSetType<T extends Set<any>> = T extends Set<infer U> ? U : never;
diff --git a/src/locales/en.json b/src/locales/en.json
index 31005cb4..f5ae003f 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -110,6 +110,7 @@
     "increaseFontSize": "Increase font size",
     "unbindText": "Unbind text",
     "bindText": "Bind text to the container",
+    "createContainerFromText": "Wrap text in a container",
     "link": {
       "edit": "Edit link",
       "create": "Create link",
diff --git a/src/tests/__snapshots__/contextmenu.test.tsx.snap b/src/tests/__snapshots__/contextmenu.test.tsx.snap
index 326cde0e..18656edd 100644
--- a/src/tests/__snapshots__/contextmenu.test.tsx.snap
+++ b/src/tests/__snapshots__/contextmenu.test.tsx.snap
@@ -119,6 +119,15 @@ Object {
           "category": "element",
         },
       },
+      Object {
+        "contextItemLabel": "labels.createContainerFromText",
+        "name": "createContainerFromText",
+        "perform": [Function],
+        "predicate": [Function],
+        "trackEvent": Object {
+          "category": "element",
+        },
+      },
       Object {
         "PanelComponent": [Function],
         "contextItemLabel": "labels.ungroup",
@@ -4507,6 +4516,15 @@ Object {
           "category": "element",
         },
       },
+      Object {
+        "contextItemLabel": "labels.createContainerFromText",
+        "name": "createContainerFromText",
+        "perform": [Function],
+        "predicate": [Function],
+        "trackEvent": Object {
+          "category": "element",
+        },
+      },
       Object {
         "PanelComponent": [Function],
         "contextItemLabel": "labels.ungroup",
@@ -5048,6 +5066,15 @@ Object {
           "category": "element",
         },
       },
+      Object {
+        "contextItemLabel": "labels.createContainerFromText",
+        "name": "createContainerFromText",
+        "perform": [Function],
+        "predicate": [Function],
+        "trackEvent": Object {
+          "category": "element",
+        },
+      },
       Object {
         "PanelComponent": [Function],
         "contextItemLabel": "labels.ungroup",
@@ -5888,6 +5915,15 @@ Object {
           "category": "element",
         },
       },
+      Object {
+        "contextItemLabel": "labels.createContainerFromText",
+        "name": "createContainerFromText",
+        "perform": [Function],
+        "predicate": [Function],
+        "trackEvent": Object {
+          "category": "element",
+        },
+      },
       Object {
         "PanelComponent": [Function],
         "contextItemLabel": "labels.ungroup",
@@ -6225,6 +6261,15 @@ Object {
           "category": "element",
         },
       },
+      Object {
+        "contextItemLabel": "labels.createContainerFromText",
+        "name": "createContainerFromText",
+        "perform": [Function],
+        "predicate": [Function],
+        "trackEvent": Object {
+          "category": "element",
+        },
+      },
       Object {
         "PanelComponent": [Function],
         "contextItemLabel": "labels.ungroup",