diff --git a/.changeset/few-items-study.md b/.changeset/few-items-study.md
new file mode 100644
index 0000000000000..6fd32b5dfa446
--- /dev/null
+++ b/.changeset/few-items-study.md
@@ -0,0 +1,7 @@
+---
+"@gradio/core": patch
+"@gradio/dataframe": patch
+"gradio": patch
+---
+
+feat:Support column/row deletion in `gr.DataFrame`
diff --git a/client/python/test/test_client.py b/client/python/test/test_client.py
index 266eddcf03324..6ac5b1bbe34d9 100644
--- a/client/python/test/test_client.py
+++ b/client/python/test/test_client.py
@@ -778,6 +778,7 @@ def __call__(self, *args, **kwargs):
 
             assert all(s in messages for s in statuses)
 
+    @pytest.mark.flaky
     @patch("gradio_client.client.Endpoint.make_end_to_end_fn")
     def test_messages_correct_two_concurrent(
         self, mock_make_end_to_end_fn, calculator_demo
diff --git a/gradio/components/dataframe.py b/gradio/components/dataframe.py
index 7e11be8037fbd..7b8d4ee679686 100644
--- a/gradio/components/dataframe.py
+++ b/gradio/components/dataframe.py
@@ -100,8 +100,8 @@ def __init__(
         Parameters:
             value: Default value to display in the DataFrame. If a Styler is provided, it will be used to set the displayed value in the DataFrame (e.g. to set precision of numbers) if the `interactive` is False. If a Callable function is provided, the function will be called whenever the app loads to set the initial value of the component.
             headers: List of str header names. If None, no headers are shown.
-            row_count: Limit number of rows for input and decide whether user can create new rows. The first element of the tuple is an `int`, the row count; the second should be 'fixed' or 'dynamic', the new row behaviour. If an `int` is passed the rows default to 'dynamic'
-            col_count: Limit number of columns for input and decide whether user can create new columns. The first element of the tuple is an `int`, the number of columns; the second should be 'fixed' or 'dynamic', the new column behaviour. If an `int` is passed the columns default to 'dynamic'
+            row_count: Limit number of rows for input and decide whether user can create new rows or delete existing rows. The first element of the tuple is an `int`, the row count; the second should be 'fixed' or 'dynamic', the new row behaviour. If an `int` is passed the rows default to 'dynamic'
+            col_count: Limit number of columns for input and decide whether user can create new columns or delete existing columns. The first element of the tuple is an `int`, the number of columns; the second should be 'fixed' or 'dynamic', the new column behaviour. If an `int` is passed the columns default to 'dynamic'
             datatype: Datatype of values in sheet. Can be provided per column as a list of strings, or for the entire sheet as a single string. Valid datatypes are "str", "number", "bool", "date", and "markdown".
             type: Type of value to be returned by component. "pandas" for pandas dataframe, "numpy" for numpy array, "polars" for polars dataframe, or "array" for a Python list of lists.
             label: the label for this component. Appears above the component and is also used as the header if there are a table of examples for this component. If None and used in a `gr.Interface`, the label will be the name of the parameter this component is assigned to.
diff --git a/js/core/src/lang/en.json b/js/core/src/lang/en.json
index 65fb919c2cde1..230198c17d3b9 100644
--- a/js/core/src/lang/en.json
+++ b/js/core/src/lang/en.json
@@ -64,6 +64,8 @@
 		"new_row": "New row",
 		"add_row_above": "Add row above",
 		"add_row_below": "Add row below",
+		"delete_row": "Delete row",
+		"delete_column": "Delete column",
 		"add_column_left": "Add column to the left",
 		"add_column_right": "Add column to the right"
 	},
diff --git a/js/dataframe/shared/Arrow.svelte b/js/dataframe/shared/Arrow.svelte
deleted file mode 100644
index 52000514b5a61..0000000000000
--- a/js/dataframe/shared/Arrow.svelte
+++ /dev/null
@@ -1,10 +0,0 @@
-<script lang="ts">
-	export let transform: string;
-</script>
-
-<svg viewBox="0 0 24 24" width="16" height="16">
-	<path
-		d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"
-		{transform}
-	/>
-</svg>
diff --git a/js/dataframe/shared/CellMenu.svelte b/js/dataframe/shared/CellMenu.svelte
index 53f9fdea9fa0e..085b7b24e9f07 100644
--- a/js/dataframe/shared/CellMenu.svelte
+++ b/js/dataframe/shared/CellMenu.svelte
@@ -1,6 +1,6 @@
 <script lang="ts">
 	import { onMount } from "svelte";
-	import Arrow from "./Arrow.svelte";
+	import CellMenuIcons from "./CellMenuIcons.svelte";
 	import type { I18nFormatter } from "js/utils/src";
 
 	export let x: number;
@@ -12,6 +12,10 @@
 	export let row: number;
 	export let col_count: [number, "fixed" | "dynamic"];
 	export let row_count: [number, "fixed" | "dynamic"];
+	export let on_delete_row: () => void;
+	export let on_delete_col: () => void;
+	export let can_delete_rows: boolean;
+	export let can_delete_cols: boolean;
 
 	export let i18n: I18nFormatter;
 	let menu_element: HTMLDivElement;
@@ -50,23 +54,35 @@
 <div bind:this={menu_element} class="cell-menu">
 	{#if !is_header && can_add_rows}
 		<button on:click={() => on_add_row_above()}>
-			<Arrow transform="rotate(-90 12 12)" />
+			<CellMenuIcons icon="add-row-above" />
 			{i18n("dataframe.add_row_above")}
 		</button>
 		<button on:click={() => on_add_row_below()}>
-			<Arrow transform="rotate(90 12 12)" />
+			<CellMenuIcons icon="add-row-below" />
 			{i18n("dataframe.add_row_below")}
 		</button>
+		{#if can_delete_rows}
+			<button on:click={on_delete_row} class="delete">
+				<CellMenuIcons icon="delete-row" />
+				{i18n("dataframe.delete_row")}
+			</button>
+		{/if}
 	{/if}
 	{#if can_add_columns}
 		<button on:click={() => on_add_column_left()}>
-			<Arrow transform="rotate(180 12 12)" />
+			<CellMenuIcons icon="add-column-left" />
 			{i18n("dataframe.add_column_left")}
 		</button>
 		<button on:click={() => on_add_column_right()}>
-			<Arrow transform="rotate(0 12 12)" />
+			<CellMenuIcons icon="add-column-right" />
 			{i18n("dataframe.add_column_right")}
 		</button>
+		{#if can_delete_cols}
+			<button on:click={on_delete_col} class="delete">
+				<CellMenuIcons icon="delete-column" />
+				{i18n("dataframe.delete_column")}
+			</button>
+		{/if}
 	{/if}
 </div>
 
@@ -110,8 +126,4 @@
 		fill: currentColor;
 		transition: fill 0.2s;
 	}
-
-	.cell-menu button:hover :global(svg) {
-		fill: var(--color-accent);
-	}
 </style>
diff --git a/js/dataframe/shared/CellMenuIcons.svelte b/js/dataframe/shared/CellMenuIcons.svelte
new file mode 100644
index 0000000000000..930423698af9d
--- /dev/null
+++ b/js/dataframe/shared/CellMenuIcons.svelte
@@ -0,0 +1,113 @@
+<script lang="ts">
+	export let icon: string;
+</script>
+
+{#if icon == "add-column-right"}
+	<svg viewBox="0 0 24 24" width="16" height="16">
+		<rect
+			x="4"
+			y="6"
+			width="4"
+			height="12"
+			stroke="currentColor"
+			stroke-width="2"
+			fill="none"
+		/>
+		<path
+			d="M12 12H19M16 8L19 12L16 16"
+			stroke="currentColor"
+			stroke-width="2"
+			fill="none"
+			stroke-linecap="round"
+		/>
+	</svg>
+{:else if icon == "add-column-left"}
+	<svg viewBox="0 0 24 24" width="16" height="16">
+		<rect
+			x="16"
+			y="6"
+			width="4"
+			height="12"
+			stroke="currentColor"
+			stroke-width="2"
+			fill="none"
+		/>
+		<path
+			d="M12 12H5M8 8L5 12L8 16"
+			stroke="currentColor"
+			stroke-width="2"
+			fill="none"
+			stroke-linecap="round"
+		/>
+	</svg>
+{:else if icon == "add-row-above"}
+	<svg viewBox="0 0 24 24" width="16" height="16">
+		<rect
+			x="6"
+			y="16"
+			width="12"
+			height="4"
+			stroke="currentColor"
+			stroke-width="2"
+		/>
+		<path
+			d="M12 12V5M8 8L12 5L16 8"
+			stroke="currentColor"
+			stroke-width="2"
+			fill="none"
+			stroke-linecap="round"
+		/>
+	</svg>
+{:else if icon == "add-row-below"}
+	<svg viewBox="0 0 24 24" width="16" height="16">
+		<rect
+			x="6"
+			y="4"
+			width="12"
+			height="4"
+			stroke="currentColor"
+			stroke-width="2"
+		/>
+		<path
+			d="M12 12V19M8 16L12 19L16 16"
+			stroke="currentColor"
+			stroke-width="2"
+			fill="none"
+			stroke-linecap="round"
+		/>
+	</svg>
+{:else if icon == "delete-row"}
+	<svg viewBox="0 0 24 24" width="16" height="16">
+		<rect
+			x="5"
+			y="10"
+			width="14"
+			height="4"
+			stroke="currentColor"
+			stroke-width="2"
+		/>
+		<path
+			d="M8 7L16 17M16 7L8 17"
+			stroke="currentColor"
+			stroke-width="2"
+			stroke-linecap="round"
+		/>
+	</svg>
+{:else if icon == "delete-column"}
+	<svg viewBox="0 0 24 24" width="16" height="16">
+		<rect
+			x="10"
+			y="5"
+			width="4"
+			height="14"
+			stroke="currentColor"
+			stroke-width="2"
+		/>
+		<path
+			d="M7 8L17 16M17 8L7 16"
+			stroke="currentColor"
+			stroke-width="2"
+			stroke-linecap="round"
+		/>
+	</svg>
+{/if}
diff --git a/js/dataframe/shared/Table.svelte b/js/dataframe/shared/Table.svelte
index 2598673a20b52..9695d1d6c27dc 100644
--- a/js/dataframe/shared/Table.svelte
+++ b/js/dataframe/shared/Table.svelte
@@ -120,13 +120,7 @@
 		id: string;
 	}[][] {
 		const data_row_length = _values.length;
-		return Array(
-			row_count[1] === "fixed"
-				? row_count[0]
-				: data_row_length < row_count[0]
-					? row_count[0]
-					: data_row_length
-		)
+		return Array(row_count[1] === "fixed" ? row_count[0] : data_row_length)
 			.fill(0)
 			.map((_, i) =>
 				Array(
@@ -791,6 +785,42 @@
 	afterUpdate(() => {
 		value_is_output = false;
 	});
+
+	async function delete_row(index: number): Promise<void> {
+		parent.focus();
+		if (row_count[1] !== "dynamic") return;
+		if (data.length <= 1) return;
+		data.splice(index, 1);
+		data = data;
+		selected = false;
+	}
+
+	async function delete_col(index: number): Promise<void> {
+		parent.focus();
+		if (col_count[1] !== "dynamic") return;
+		if (data[0].length <= 1) return;
+
+		_headers.splice(index, 1);
+		_headers = _headers;
+
+		data.forEach((row) => {
+			row.splice(index, 1);
+		});
+		data = data;
+		selected = false;
+	}
+
+	function delete_row_at(index: number): void {
+		delete_row(index);
+		active_cell_menu = null;
+		active_header_menu = null;
+	}
+
+	function delete_col_at(index: number): void {
+		delete_col(index);
+		active_cell_menu = null;
+		active_header_menu = null;
+	}
 </script>
 
 <svelte:window on:resize={() => set_cell_widths()} />
@@ -1050,18 +1080,22 @@
 	</div>
 </div>
 
-{#if active_cell_menu !== null}
+{#if active_cell_menu}
 	<CellMenu
-		{i18n}
 		x={active_cell_menu.x}
 		y={active_cell_menu.y}
-		row={active_cell_menu?.row ?? -1}
+		row={active_cell_menu.row}
 		{col_count}
 		{row_count}
-		on_add_row_above={() => add_row_at(active_cell_menu?.row ?? -1, "above")}
-		on_add_row_below={() => add_row_at(active_cell_menu?.row ?? -1, "below")}
-		on_add_column_left={() => add_col_at(active_cell_menu?.col ?? -1, "left")}
-		on_add_column_right={() => add_col_at(active_cell_menu?.col ?? -1, "right")}
+		on_add_row_above={() => add_row_at(active_cell_menu?.row || 0, "above")}
+		on_add_row_below={() => add_row_at(active_cell_menu?.row || 0, "below")}
+		on_add_column_left={() => add_col_at(active_cell_menu?.col || 0, "left")}
+		on_add_column_right={() => add_col_at(active_cell_menu?.col || 0, "right")}
+		on_delete_row={() => delete_row_at(active_cell_menu?.row || 0)}
+		on_delete_col={() => delete_col_at(active_cell_menu?.col || 0)}
+		can_delete_rows={data.length > 1}
+		can_delete_cols={data[0].length > 1}
+		{i18n}
 	/>
 {/if}
 
@@ -1078,6 +1112,10 @@
 		on_add_column_left={() => add_col_at(active_header_menu?.col ?? -1, "left")}
 		on_add_column_right={() =>
 			add_col_at(active_header_menu?.col ?? -1, "right")}
+		on_delete_row={() => delete_row_at(active_cell_menu?.row ?? -1)}
+		on_delete_col={() => delete_col_at(active_header_menu?.col ?? -1)}
+		can_delete_rows={false}
+		can_delete_cols={data[0].length > 1}
 	/>
 {/if}