diff --git a/.changeset/FN-6958-planning-focus.md b/.changeset/FN-6958-planning-focus.md
new file mode 100644
index 0000000000..4e2c405b4e
--- /dev/null
+++ b/.changeset/FN-6958-planning-focus.md
@@ -0,0 +1,5 @@
+---
+"@runfusion/fusion": patch
+---
+
+Stop Planning Mode from automatically focusing the initial text entry when it opens, preventing mobile keyboards from appearing until the user explicitly focuses the textarea.
diff --git a/packages/dashboard/app/components/PlanningModeModal.tsx b/packages/dashboard/app/components/PlanningModeModal.tsx
index f86a01efb1..49bcdb204a 100644
--- a/packages/dashboard/app/components/PlanningModeModal.tsx
+++ b/packages/dashboard/app/components/PlanningModeModal.tsx
@@ -821,12 +821,10 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, onTasksCreat
projectId,
]);
- // Focus textarea when opening
- useEffect(() => {
- if (isOpen && view.type === "initial") {
- textareaRef.current?.focus();
- }
- }, [isOpen, view.type]);
+ /*
+ FNXC:PlanningFocus 2026-06-23-00:00:
+ Viewing Planning Mode must not auto-focus the initial composer because mobile browsers open the keyboard before the user chooses to type. Keep the textarea ref for autosize and explicit user focus only; populated initialPlan handoffs still auto-start through the separate effect below.
+ */
useEffect(() => {
if (!isOpen) {
diff --git a/packages/dashboard/app/components/__tests__/PlanningModeModal.initial.test.tsx b/packages/dashboard/app/components/__tests__/PlanningModeModal.initial.test.tsx
index 938854a6e0..0675fd0274 100644
--- a/packages/dashboard/app/components/__tests__/PlanningModeModal.initial.test.tsx
+++ b/packages/dashboard/app/components/__tests__/PlanningModeModal.initial.test.tsx
@@ -216,6 +216,75 @@ describe("PlanningModeModal", () => {
expect(screen.queryByText("Planning Mode")).toBeNull();
});
+ it("does not auto-focus the initial textarea on mobile open until the user focuses it", () => {
+ mockViewport("mobile");
+
+ render(
+
+ );
+
+ const textarea = screen.getByLabelText("What do you want to build?") as HTMLTextAreaElement;
+ expect(document.activeElement).not.toBe(textarea);
+
+ act(() => {
+ textarea.focus();
+ });
+
+ expect(document.activeElement).toBe(textarea);
+ });
+
+ it("does not auto-focus the initial textarea in embedded desktop presentation", () => {
+ render(
+
+ );
+
+ const textarea = screen.getByLabelText("What do you want to build?") as HTMLTextAreaElement;
+ expect(textarea.value).toBe("");
+ expect(document.activeElement).not.toBe(textarea);
+ });
+
+ it("auto-starts populated initialPlan handoffs without focusing the initial textarea", async () => {
+ const focusSpy = vi.spyOn(HTMLTextAreaElement.prototype, "focus");
+
+ try {
+ render(
+
+ );
+
+ await waitFor(() => {
+ expect(mockStartPlanningStreaming).toHaveBeenCalledWith("Build a login system from handoff", undefined, undefined, {
+ planningDepth: "medium",
+ customQuestionCount: undefined,
+ }, undefined);
+ });
+
+ expect(focusSpy).not.toHaveBeenCalled();
+ } finally {
+ focusSpy.mockRestore();
+ }
+ });
+
it("mobile close path blurs focused input and resets viewport scroll", () => {
mockViewport("mobile");
const scrollToSpy = vi.spyOn(window, "scrollTo").mockImplementation(() => undefined);