The document editor shows that the instance already exists and it skips loading, but the docEditorReference returns nul

WhatsApp Image 2025-07-09 at 21.12.51_6df156b4

We are able to get the docx file running in only office doc editor component but constantly getting the docRef as null due to which we are not able to work.

The document editor shows that the instance already exists and it skips loading, but the docEditorReference returns null.

“use client”;

import { useEffect, useRef, useState, useMemo, useCallback } from “react”;
import “quill/dist/quill.snow.css”;
import * as mammoth from “mammoth”;
import * as htmlDocx from “html-docx-js/dist/html-docx”;
import {
ref as storageRef,
getDownloadURL,
listAll,
} from “firebase/storage”;
import { storage } from “@/lib/firebase”;
import “./quill-fonts.css”;
import { HoverChat } from “./hover-chat”;
import { useAuth } from “@/contexts/auth-context”;
import { useToast } from “@/components/ui/use-toast”;
import { Button } from “@/components/ui/button”;
import { DocumentEditor } from “@onlyoffice/document-editor-react”;
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
} from “@/components/ui/dropdown-menu”;
import { ChevronDown } from “lucide-react”;

export default function EditorTogglePage() {
const editorRef = useRef(null);
const quillRef = useRef(null);
const onlyOfficeRef = useRef(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [activeEditor, setActiveEditor] = useState<“quill” | “onlyoffice”>(“quill”);
const { user } = useAuth();
const { toast } = useToast();

const [token, setToken] = useState<string | null>(null);
const [editorReady, setEditorReady] = useState(false);
const [showSaveDialog, setShowSaveDialog] = useState(false);
const [savedFileName, setSavedFileName] = useState(“”);
const [hoverChat, setHoverChat] = useState(null);

const searchParams = typeof window !== “undefined” ? new URLSearchParams(window.location.search) : null;
const docUrl = searchParams?.get(“file”) || null;

const docSessionKey = useMemo(() => {
if (!docUrl) return new-doc-${Date.now()};
const baseKey = btoa(docUrl).slice(0, 20).replace(/[^a-zA-Z0-9]/g, “”);
return doc-${baseKey}-${Date.now()};
}, [docUrl]);

function getFileNameFromUrl(url: string | null) {
if (!url) return “”;
try {
const decoded = decodeURIComponent(url);
const lastSegment = decoded.split(“/”).pop() || “”;
return lastSegment.split(“?”)[0];
} catch {
return “”;
}
}

// Quill Editor Initialization
useEffect(() => {
if (activeEditor !== “quill”) return;
const initializeEditor = async () => {
try {
const Quill = (await import(“quill”)).default;
const FontAttributor: any = Quill.import(“attributors/class/font”);
FontAttributor.whitelist = [“arial”, “times-new-roman”, “verdana”];
Quill.register(FontAttributor, true);

    if (!editorRef.current) return;

    quillRef.current = new Quill(editorRef.current, {
      theme: "snow",
      modules: {
        toolbar: [
          [{ header: [1, 2, 3, false] }],
          [{ font: FontAttributor.whitelist }],
          ["bold", "italic", "underline", "strike"],
          ["blockquote", "code-block"],
          [{ list: "ordered" }, { list: "bullet" }],
          [{ align: [] }],
          ["link", "image"],
          ["clean"],
        ],
      },
    });
  } catch (e) {
    setError("Failed to load editor");
  } finally {
    setLoading(false);
  }
};

initializeEditor();

}, [activeEditor]);

// UI
return (



<Button onClick={() => toast({ title: “Save Clicked” })} variant=“primary” size=“sm”>Save
<Button onClick={() => setActiveEditor(“quill”)} variant={activeEditor === “quill” ? “default” : “outline”} size=“sm”>Quill
<Button onClick={() => setActiveEditor(“onlyoffice”)} variant={activeEditor === “onlyoffice” ? “default” : “outline”} size=“sm”>OnlyOffice




Document




<DropdownMenuItem onClick={() => toast({ title: “Version 1” })}>Version 1
<DropdownMenuItem onClick={() => toast({ title: “Version 2” })}>Version 2



  <div className="flex-1 min-h-0 flex flex-col">
    {activeEditor === "quill" && (
      <div ref={editorRef} style={{ height: "100%", minHeight: 0, border: "1px solid #ccc", flex: 1 }} />
    )}
    {activeEditor === "onlyoffice" && (
      <div style={{ height: "100%", minHeight: 0, flex: 1 }}>
        <DocumentEditor
          id="onlyoffice-editor"
          key={docSessionKey}
          documentServerUrl="YOUR_DOCUMENT_SERVER_URL"
          config={{
            document: {
              fileType: "docx",
              key: docSessionKey,
              title: getFileNameFromUrl(docUrl),
              url: docUrl || "",
              permissions: { edit: true, download: true },
            },
            editorConfig: {
              user: {
                id: user?.uid || "anonymous",
                name: user?.displayName || "User",
              },
              customization: {
                autosave: true,
                forcesave: true,
                uiTheme: "dark",
              },
            },
            token,
            width: "100%",
            height: "100%",
          }}
          onLoad={(editor) => {
            onlyOfficeRef.current = editor;
            setEditorReady(true);
          }}
        />
      </div>
    )}
  </div>

  {showSaveDialog && (

    <div className="fixed inset-0 flex items-center justify-center z-50 bg-black/30">
      <div className="bg-white dark:bg-gray-900 rounded-lg shadow-lg p-6 min-w-[300px] flex flex-col items-center">
        <span className="text-2xl mb-2">✅</span>
        <div className="font-semibold mb-1">Document Saved!</div>
        <div className="text-xs text-muted-foreground mb-2">{savedFileName}</div>
        <div className="text-sm text-gray-500">Reloading...</div>
      </div>
    </div>
  )}
</div>

);
}

Hello @amritsundarka
Please provide us with additional details. Do I understand it right that you’re trying to use React framework? React | ONLYOFFICE
If so, have you tried to deploy our sample?

As for the situation, please try to re-initialize the editor, probably this method will be useful: Methods | ONLYOFFICE
Will the situation change?
If I misunderstood the request, please provide us with entire scenario.

I’m working on integrating the ONLYOFFICE Document Editor into my Next.js application and have run into an issue where I’m unable to get a stable reference to the editor instance. My goal is to use the docEditorRef to call API methods like executeCommand(‘forcesave’) and destroyEditor(), but the ref’s current value is always null when I try to access it.

I’m using a useRef hook (docEditorRef) to hold the editor instance. I’m assigning the instance to the ref within the onAppReady event handler, which seems to be the correct approach. However, when my handleOnlyOfficeForceSave function is triggered by a button click, the console shows that docEditorRef.current is null.
This is the error I see in the console:

The same issue occurs in my component’s cleanup effect, where a call to docEditorRef.current.destroyEditor() also fails because the ref is null.
Environment & Versions
• Framework: Next.js 15.2.4
• React: (18.2.0)
• ONLYOFFICE Wrapper: @onlyoffice/document-editor-react (2.0.0)
Relevant Code
Here are the key parts of my EditorTogglePage component:

  1. Ref Initialization and State
    import { DocumentEditor } from “@onlyoffice/document-editor-react”;
    import { useRef, useState, useEffect } from “react”;

// … inside the component
const docEditorRef = useRef(null); // This will hold DocsAPI.DocEditor
const [isEditorReady, setIsEditorReady] = useState(false);

  1. The DocumentEditor Component Implementation
    This is how I’m rendering the editor. I’ve confirmed that the docSessionKey and token are being generated correctly. The editor itself loads and is usable.
    <DocumentEditor
    id=“onlyoffice-editor”
    key={docSessionKey}
    documentServerUrl={documentServerUrl}
    config={/* See config object below */}
    events={{
    onAppReady: function () {
    console.log(“[OO] onAppReady fired, editor captured.”);
    docEditorRef.current = this; // “this” should be the editor instance
    setIsEditorReady(true);
    },
    onDownloadAs,
    }}
    />

  2. ONLYOFFICE Configuration Object (config)
    {
    document: {
    fileType: “docx”,
    key: docSessionKey, // a unique key generated for the session
    title: “document.docx”,
    url: “https://your-document-url.com/…”, // HTTPS URL to the document
    permissions: { edit: true, download: true },
    },
    editorConfig: {
    user: {
    id: user.uid,
    name: user.displayName || user.email || “User”,
    },
    customization: {
    autosave: true,
    forcesave: true,
    uiTheme: “dark”,
    },
    callbackUrl: “https://your-backend-url/api/onlyoffice-callback”,
    },
    token, // JWT token
    width: “100%”,
    height: “100%”,
    }

  3. The forceSave Handler
    This is the function that fails because docEditorRef.current is null.
    const handleOnlyOfficeForceSave = () => {
    const editor = docEditorRef.current;
    console.log(“docEditorRef”, docEditorRef); // Logs { current: null }
    console.log("docEditorRef.current : ", docEditorRef.current); // Logs null
    console.log("Editor: ", editor); // Logs null

if (!editor) {
toast({
variant: “destructive”,
title: “Editor not ready or ref is missing.”,
});
console.warn(“[OO] Force save failed because docEditorRef.current is null.”);
return;
}

if (typeof editor.executeCommand === “function”) {
editor.executeCommand(“forcesave”);
toast({ title: “:outbox_tray: Force save requested” });
}
};

  1. The destroyEditor Cleanup Attempt
    This useEffect also fails to find the editor instance on component unmount.
    useEffect(() => {
    // This function will run when the component is unmounted
    return () => {
    if (docEditorRef.current) {
    console.log(“[Cleanup] Destroying OnlyOffice editor instance…”);
    docEditorRef.current.destroyEditor();
    docEditorRef.current = null;
    }
    };
    }, );

My Question

What could be causing the docEditorRef.current to be null outside of the onAppReady callback? It seems like the reference is not persisting across React renders or is being reset somehow. Is there a better or more reliable way to maintain a reference to the editor instance in a Next.js/React functional component?

Hello @amritsundarka
Sorry for the late reply. Probably we found an issue in the provided code

No, this is how you should get the editor instance: docEditorRef.current = window.DocEditor.instances["docxEditor"];
here’s useful link: React | ONLYOFFICE

Really grateful for the help

We are still getting, “Skip loading. Instance already exists onlyoffice-editor”

“use client”;

import { useEffect, useRef, useState, useMemo, useCallback } from “react”;
import “quill/dist/quill.snow.css”;
import * as mammoth from “mammoth”;
import * as htmlDocx from “html-docx-js/dist/html-docx”;
import { ref as storageRef, getDownloadURL, listAll } from “firebase/storage”;
import { storage } from “@/lib/firebase”;
import “./quill-fonts.css”;
import { HoverChat } from “./hover-chat”;
import { useAuth } from “@/contexts/auth-context”;
import { useToast } from “@/components/ui/use-toast”;
import { Button } from “@/components/ui/button”;
import { DocumentEditor } from “@onlyoffice/document-editor-react”;
import { SignJWT } from “jose”;
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
} from “@/components/ui/dropdown-menu”;
import { ChevronDown } from “lucide-react”;

// Define the DocEditor object on the window for TypeScript
declare global {
interface Window {
DocEditor: any;
}
}

export default function EditorTogglePage() {
// — Shared State —
const CALLBACK_URL = https://alfa-law-backend-28353329838.asia-south2.run.app/api/onlyoffice-callback;

const editorRef = useRef(null);
const quillRef = useRef(null);
const docEditorRef = useRef(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [currentFileUrl, setCurrentFileUrl] = useState<string | null>(null);
const [baseFileName, setBaseFileName] = useState(“document”);
const [userId, setUserId] = useState(“unknown-user”);

const [hoverChat, setHoverChat] = useState<{
visible: boolean;
position: { x: number; y: number };
selectedText: string;
} | null>(null);
const [activeEditor, setActiveEditor] = useState<“quill” | “onlyoffice”>(
“quill”
);

const { user } = useAuth();
const { toast } = useToast();

// — OnlyOffice State —
const [token, setToken] = useState<string | null>(null);

// — Version Dropdown State —
const [fileVersions, setFileVersions] = useState<
{ name: string; downloadURL: string }

();
const [showSaveDialog, setShowSaveDialog] = useState(false);
const [savedFileName, setSavedFileName] = useState(“”);

// — OnlyOffice Config —
const searchParams =
typeof window !== “undefined”
? new URLSearchParams(window.location.search)
: null;
const docUrl = searchParams?.get(“file”) || currentFileUrl;
const documentServerUrl = process.env.NEXT_PUBLIC_DOCUMENTURL;

const docSessionKey = useMemo(() => {
if (!docUrl) return new-doc-${Date.now()};
const baseKey = btoa(docUrl)
.slice(0, 20)
.replace(/[^a-zA-Z0-9]/g, “”);
return doc-${baseKey}-${Date.now()};
}, [docUrl]);

const cacheBustedUrl = useMemo(() => {
if (!docUrl) return “”;
return ${docUrl}${docUrl.includes("?") ? "&" : "?"}t=${Date.now()};
}, [docUrl]);

// — Helper Functions —
function displayName(name: string) {
const idx = name.indexOf(“_”);
return idx !== -1 ? name.slice(idx + 1) : name;
}

function getFileNameFromUrl(url: any) {
if (!url) return “”;
try {
const decoded = decodeURIComponent(url);
const lastSegment = decoded.split(“/”).pop() || “”;
return lastSegment.split(“?”)[0];
} catch {
return “”;
}
}

// — Lifecycle Hooks —
useEffect(() => {
console.log(
[Effect] activeEditor is now: ${activeEditor}. This effect will run its cleanup when this changes.
);

// Return the cleanup function
return () => {
  console.log(
    `[Cleanup] activeEditor is about to change from: ${activeEditor}. Running cleanup.`
  );

  const editorId = "onlyoffice-editor";
  // Check if the global DocEditor object and its instances map exist
  if (
    window.DocEditor &&
    window.DocEditor.instances &&
    window.DocEditor.instances[editorId]
  ) {
    console.log(
      `[Cleanup] Found instance '${editorId}'. Calling destroyEditor() on it.`
    );
    try {
      window.DocEditor.instances[editorId].destroyEditor();
      console.log(
        `[Cleanup] Successfully destroyed instance '${editorId}'.`
      );
    } catch (e) {
      console.error(
        `[Cleanup] Error destroying instance '${editorId}':`,
        e
      );
    }
  } else {
    console.log(
      `[Cleanup] Instance '${editorId}' not found in window.DocEditor.instances. No action needed or already destroyed.`
    );
  }

  // Also clear our React ref to be safe
  if (docEditorRef.current) {
    console.log("[Cleanup] Clearing docEditorRef.current.");
    docEditorRef.current = null;
  }
};

}, [activeEditor]);

useEffect(() => {
if (!userId || !baseFileName) return;
const fetchVersions = async () => {
try {
const folderRef = storageRef(storage, documents/${userId}/);
const listResult = await listAll(folderRef);
const versions: { name: string; downloadURL: string } = ;
for (const item of listResult.items) {
if (
item.name === ${baseFileName}.docx ||
item.name.match(new RegExp(^${baseFileName}-v\\d+\\.docx$))
) {
const url = await getDownloadURL(item);
versions.push({ name: item.name, downloadURL: url });
}
}
versions.sort((a, b) => {
const av = a.name.match(/-v(\d+).docx$/)?.[1];
const bv = b.name.match(/-v(\d+).docx$/)?.[1];
if (!av && !bv) return 0;
if (!av) return -1;
if (!bv) return 1;
return Number(av) - Number(bv);
});
setFileVersions(versions);
} catch (err) {
console.error(“Failed to fetch versions:”, err);
setFileVersions();
}
};
fetchVersions();
}, [userId, baseFileName]);

// — Quill Editor Initialization —
useEffect(() => {
if (activeEditor !== “quill”) return;
console.log(“[Effect] Initializing Quill editor.”);
const initializeEditor = async () => {
try {
const Quill = (await import(“quill”)).default;
const FontAttributor: any = Quill.import(“attributors/class/font”);
const fontWhitelist = [“arial”, “times-new-roman”, “verdana”];
FontAttributor.whitelist = fontWhitelist;
Quill.register(FontAttributor, true);

    if (!editorRef.current) return;

    quillRef.current = new Quill(editorRef.current, {
      theme: "snow",
      modules: {
        toolbar: [
          [{ header: [1, 2, 3, false] }],
          [{ font: fontWhitelist }],
          ["bold", "italic", "underline", "strike"],
          ["blockquote", "code-block"],
          [{ list: "ordered" }, { list: "bullet" }],
          [{ align: [] }],
          ["link", "image"],
          ["clean"],
        ],
      },
    });

    const urlParams = new URLSearchParams(window.location.search);
    const fileUrl = urlParams.get("file");
    setCurrentFileUrl(fileUrl);

    if (!fileUrl) {
      setLoading(false);
      return;
    }

    const decodedUrl = decodeURIComponent(fileUrl);
    const userIdMatch = decodedUrl.match(/documents\/([^\/]+)\//);
    const extractedUserId = userIdMatch ? userIdMatch[1] : "unknown-user";
    setUserId(extractedUserId);

    const fileNameWithExt =
      decodedUrl.split("/").pop()?.split("?")[0] || "document.docx";
    const fileNameWithoutExt = fileNameWithExt.replace(/\.docx$/, "");
    const extractedFileName = fileNameWithoutExt.replace(/-v\d+$/, "");
    setBaseFileName(extractedFileName);

    const deltaPath = `documents/${extractedUserId}/${fileNameWithoutExt}.json`;

    try {
      const deltaRef = storageRef(storage, deltaPath);
      const deltaUrl = await getDownloadURL(deltaRef);
      const response = await fetch(deltaUrl);
      const deltaJson = await response.json();
      quillRef.current.setContents(deltaJson);
    } catch (deltaError) {
      const response = await fetch(fileUrl);
      const arrayBuffer = await response.arrayBuffer();
      const result = await mammoth.convertToHtml(
        { arrayBuffer },
        {
          styleMap: [
            "p[style-name='Title'] => h1:fresh",
            "p[style-name='Heading 1'] => h1:fresh",
            "p[style-name='Heading 2'] => h2:fresh",
            "p[style-name='Heading 3'] => h3:fresh",
          ],
          convertImage: mammoth.images.imgElement((image: any) =>
            image.read("base64").then((imageBuffer: any) => ({
              src: `data:${image.contentType};base64,${imageBuffer}`,
            }))
          ),
        }
      );
      const html = result.value;
      const delta = quillRef.current.clipboard.convert({ html });
      quillRef.current.setContents(delta, "silent");
    }
  } catch (e) {
    setError("Failed to load document: " + (e as Error).message);
  } finally {
    setLoading(false);
  }
};

initializeEditor();

}, [activeEditor]);

// — UI Toggles —
useEffect(() => {
if (editorRef.current) {
const toolbar = editorRef.current.querySelector(“.ql-toolbar”);
if (toolbar) {
(toolbar as HTMLElement).style.display =
activeEditor === “quill” ? “block” : “none”;
}
}
}, [activeEditor]);

// — OnlyOffice JWT Generation —
const generateToken = useCallback(async () => {
if (!user || !docUrl) return;
console.log(“[Token] Generating JWT for ONLYOFFICE.”);
const secret = new TextEncoder().encode(
process.env.NEXT_PUBLIC_JWT_SECRET!
);

const jwt = await new SignJWT({
  document: {
    fileType: "docx",
    key: docSessionKey,
    title: docUrl?.split("/").pop() || "document.docx",
    url: cacheBustedUrl,
    permissions: { edit: true, download: true },
  },
  editorConfig: {
    callbackUrl: CALLBACK_URL,
    user: { id: user.uid, name: user.displayName || user.email || "User" },
    customization: { autosave: true, forcesave: true, uiTheme: "dark" },
  },
  iat: Math.floor(Date.now() / 1000),
})
  .setProtectedHeader({ alg: "HS256" })
  .sign(secret);

setToken(jwt);
console.log("[Token] JWT generated and set in state.");

}, [user, docUrl, docSessionKey, cacheBustedUrl, CALLBACK_URL]);

useEffect(() => {
if (user && docUrl && activeEditor === “onlyoffice”) {
generateToken();
}
}, [user, docUrl, generateToken, activeEditor]);

// — Save Logic —
const isEditorEmpty = () => {
const contents = quillRef.current?.getContents();
if (!contents) return true;
return (
contents.ops.length === 1 &&
contents.ops[0].insert === “\n” &&
!Object.keys(contents.ops[0].attributes || {}).length
);
};

const getNextVersionNumber = async (
userId: string,
baseFileName: string
): Promise => {
try {
const folderRef = storageRef(storage, documents/${userId}/);
const listResult = await listAll(folderRef);
const existingVersions: number = ;
listResult.items.forEach((item) => {
const fileName = item.name;
const versionMatch = fileName.match(
new RegExp(
^${baseFileName.replace( /[.*+?^${}()|[\]\\]/g, "\\$&" )}-v(\\d+)\\.(docx|json)$
)
);
if (versionMatch) {
existingVersions.push(parseInt(versionMatch[1]));
}
});
const maxVersion =
existingVersions.length > 0 ? Math.max(…existingVersions) : 0;
return maxVersion + 1;
} catch (error) {
console.error(“Error getting next version number:”, error);
return 1;
}
};

const handleSave = async () => {
if (activeEditor === “quill”) {
if (!quillRef.current || !currentFileUrl) {
toast({
variant: “destructive”,
title: “:x: Missing file or editor reference.”,
});
return;
}
if (isEditorEmpty()) {
toast({ variant: “destructive”, title: “Cannot save empty document” });
return;
}
try {
setLoading(true);
const nextVersion = await getNextVersionNumber(userId, baseFileName);
const delta = quillRef.current.getContents();
const innerHtml = quillRef.current.root.innerHTML;

    const htmlContent = `
    <!DOCTYPE html><html><head><meta charset="utf-8"><title>${baseFileName}</title>
    <style>
      body { font-family: Arial, sans-serif; } h1, h2, h3 { font-weight: bold; }
      p { margin: 0 0 10px; } .ql-align-center { text-align: center; }
      .ql-align-right { text-align: right; } .ql-align-left { text-align: left; }
    </style></head><body>${innerHtml}</body></html>`;

    const docxBlob = htmlDocx.asBlob(htmlContent);
    const deltaBlob = new Blob([JSON.stringify(delta, null, 2)], {
      type: "application/json",
    });

    const newDocxFileName = `${baseFileName}-v${nextVersion}.docx`;
    const newDeltaFileName = `${baseFileName}-v${nextVersion}.json`;

    const decodedUrl = decodeURIComponent(currentFileUrl || "");
    const pathMatch = decodedUrl.match(/documents\/[^/]+\/(.+\/)?/);
    let folderPath = `documents/${userId}/`;
    if (pathMatch) {
      folderPath = decodedUrl.substring(
        decodedUrl.indexOf("documents/"),
        decodedUrl.lastIndexOf("/") + 1
      );
    }

    const idToken = await user?.getIdToken();
    if (!idToken) {
      throw new Error("User not authenticated.");
    }

    // Upload .docx
    const docxFormData = new FormData();
    docxFormData.append("file", docxBlob, newDocxFileName);
    docxFormData.append("path", folderPath);
    const docxRes = await fetch(
      "https://alfa-law-backend-28353329838.asia-south2.run.app/api/documents/upload",
      {
        method: "POST",
        headers: { Authorization: `Bearer ${idToken}` },
        body: docxFormData,
      }
    );
    const docxData = await docxRes.json();
    if (!docxRes.ok || !docxData.success) {
      throw new Error(docxData?.message || "DOCX upload failed");
    }

    // Upload .json
    const jsonFormData = new FormData();
    jsonFormData.append("file", deltaBlob, newDeltaFileName);
    jsonFormData.append("path", folderPath);
    const jsonRes = await fetch(
      "https://alfa-law-backend-28353329838.asia-south2.run.app/api/documents/upload",
      {
        method: "POST",
        headers: { Authorization: `Bearer ${idToken}` },
        body: jsonFormData,
      }
    );
    const jsonData = await jsonRes.json();
    if (!jsonRes.ok || !jsonData.success) {
      throw new Error(jsonData?.message || "JSON delta upload failed");
    }

    setSavedFileName(newDocxFileName);
    setShowSaveDialog(true);

    const newFileRef = storageRef(
      storage,
      `${folderPath}${newDocxFileName}`
    );
    const newFileUrl = await getDownloadURL(newFileRef);

    setTimeout(() => {
      setShowSaveDialog(false);
      window.location.href = `/alfa-chat?file=${encodeURIComponent(
        newFileUrl
      )}`;
    }, 2000);

    toast({ title: `✅ Document saved as version ${nextVersion}` });
  } catch (err: any) {
    toast({
      variant: "destructive",
      title: "❌ Save error",
      description: err.message,
    });
  } finally {
    setLoading(false);
  }
} else if (activeEditor === "onlyoffice") {
  handleOnlyOfficeForceSave();
}

};

const onDownloadAs = async (e: any) => {
const { url, fileType } = e.data; // DOCX link from DS
try {
const blob = await fetch(url).then((r) => r.blob());
const fd = new FormData();
fd.append(“file”, blob, ${baseFileName}.${fileType});

  const idToken = await user?.getIdToken();
  await fetch(
    "https://alfa-law-backend-28353329838.asia-south2.run.app/api/documents/upload",
    {
      method: "POST",
      headers: { Authorization: `Bearer ${idToken}` },
      body: fd,
    }
  );

  toast({ title: "✅ Saved to backend" });
} catch (err: any) {
  toast({
    variant: "destructive",
    title: "❌ Upload failed",
    description: err.message,
  });
}

};

const handleOnlyOfficeForceSave = () => {
console.log(
“[Force Save] Button clicked. Current editor ref:”,
docEditorRef.current
);
const editor = docEditorRef.current;
if (!editor) {
toast({ variant: “destructive”, title: “Editor not ready” });
console.warn(“[Force Save] Aborted: editor instance is null.”);
return;
}
if (typeof editor.executeCommand === “function”) {
console.log(“[Force Save] Executing ‘forcesave’ command…”);
const ok = editor.executeCommand(“forcesave”);
console.log(“[Force Save] Command executed, result:”, ok);
if (!ok) {
toast({ variant: “destructive”, title: “Nothing to save” });
} else {
toast({ title: “:outbox_tray: Force save requested” });
}
} else {
toast({ variant: “destructive”, title: “Force-save API unavailable” });
console.error(
“[Force Save] Aborted: editor.executeCommand is not a function.”
);
}
};

// — Keyboard Shortcut Logic —
const handleHoverChatSend = (message: string) => {
console.log(“Hover chat message sent:”, message);
toast({
title: “:white_check_mark: Chat Sent!”,
description: Message: "${message}",
});
setHoverChat(null);
};

useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (activeEditor !== “quill” || !quillRef.current) return;
const isCtrlOrCmd = e.ctrlKey || e.metaKey;

  if (isCtrlOrCmd && e.key === "m") {
    e.preventDefault();
    const selection = quillRef.current.getSelection();
    if (!selection || selection.length === 0) return;
    const text = quillRef.current.getText(
      selection.index,
      selection.length
    );
    if (!text.trim()) return;
    const bounds = quillRef.current.getBounds(selection.index);
    setHoverChat({
      visible: true,
      position: { x: bounds.left, y: bounds.top + bounds.height },
      selectedText: text,
    });
  }

  if (isCtrlOrCmd && e.key === "g") {
    e.preventDefault();
    const selection = quillRef.current.getSelection();
    if (!selection || selection.length === 0) {
      toast({ variant: "destructive", title: "No text selected" });
      return;
    }
    const text = quillRef.current.getText(
      selection.index,
      selection.length
    );
    if (text.trim()) {
      localStorage.setItem("editorselectedText", text);
      toast({
        title: "✅ Text Saved to Local Storage",
        description: `Saved ${text.length} characters.`,
      });
    }
  }
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);

}, [activeEditor, toast]);

// — UI —
return (


{/* Top Buttons */}


Save

<Button
onClick={() => {
console.log(
“[Click] ‘Open with Quill’ button clicked. Changing activeEditor to ‘quill’.”
);
setActiveEditor(“quill”);
}}
variant={activeEditor === “quill” ? “default” : “outline”}
size=“sm”
className=“text-xs px-2 py-1 h-7 min-w-0”
>
Open with Quill

<Button
onClick={() => {
console.log(
“[Click] ‘Open with OnlyOffice’ button clicked. Changing activeEditor to ‘onlyoffice’.”
);
setActiveEditor(“onlyoffice”);
}}
variant={activeEditor === “onlyoffice” ? “default” : “outline”}
size=“sm”
className=“text-xs px-2 py-1 h-7 min-w-0”
>
Open with OnlyOffice

{activeEditor === “onlyoffice” && (

Force Save

)}

    <div className="flex-1 flex justify-end items-center gap-2">
      {fileVersions.length > 0 && (
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button
              variant="ghost"
              size="sm"
              className="flex items-center text-xs px-2 py-1 h-7 min-w-0"
            >
              <span className="truncate max-w-[120px]">
                {displayName(baseFileName)}
              </span>
              <span className="ml-2">
                {(() => {
                  const urlParams = new URLSearchParams(
                    window.location.search
                  );
                  const fileUrl = urlParams.get("file");
                  const decodedUrl = fileUrl
                    ? decodeURIComponent(fileUrl)
                    : "";
                  const currentVersion = fileVersions.find((v) =>
                    decodedUrl.includes(v.name)
                  );
                  let label = "Version Base";
                  const match =
                    currentVersion?.name.match(/-v(\d+)\.docx$/i);
                  if (match) label = `Version ${match[1]}`;
                  return label;
                })()}
              </span>
              <ChevronDown className="h-3 w-3 ml-1" />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            {fileVersions.map((version) => {
              let label = "Version Base";
              const match = version.name.match(/-v(\d+)\.docx$/i);
              if (match) label = `Version ${match[1]}`;
              return (
                <DropdownMenuItem
                  key={version.name}
                  onClick={() => {
                    const encodedURL = encodeURIComponent(
                      version.downloadURL
                    );
                    window.location.href = `/alfa-chat?file=${encodedURL}`;
                  }}
                >
                  <span className="truncate max-w-[120px]">
                    {displayName(baseFileName)}
                  </span>
                  <span className="ml-2">{label}</span>
                </DropdownMenuItem>
              );
            })}
          </DropdownMenuContent>
        </DropdownMenu>
      )}
    </div>
  </div>

  {/* Editor Containers */}
  <div className="flex-1 min-h-0 flex flex-col">
    {activeEditor === "quill" && (
      <>
        {loading && (
          <div className="text-center py-8">Loading document...</div>
        )}
        {error && (
          <div className="text-red-500 text-center py-8">{error}</div>
        )}
        {hoverChat?.visible && (
          <HoverChat
            position={hoverChat.position}
            selectedText={hoverChat.selectedText}
            onClose={() => setHoverChat(null)}
            onSend={handleHoverChatSend}
          />
        )}
        <div
          ref={editorRef}
          style={{
            height: "100%",
            minHeight: 0,
            border: "1px solid #ccc",
            flex: 1,
            display: loading ? "none" : "block",
          }}
          className={activeEditor !== "quill" ? "hide-quill-toolbar" : ""}
        />
      </>
    )}
    {activeEditor === "onlyoffice" && (
      <>
        <style>
          {`
            .ql-toolbar {
              display: none !important;
            }
          `}
        </style>
        {!documentServerUrl ? (
          <div className="p-4 text-red-500">
            ❌ Document server URL not configured
          </div>
        ) : !user ? (
          <div className="p-4">🔒 Please login to edit documents</div>
        ) : !token ? (
          <div className="p-4">⏳ Preparing document...</div>
        ) : (
          <div style={{ height: "100%", minHeight: 0, flex: 1 }}>
            {console.log(
              `[Render] Rendering ONLYOFFICE. User: ${!!user}, Token: ${!!token}, URL: ${docUrl}`
            )}
            <DocumentEditor
              id="onlyoffice-editor"
              key={docSessionKey}
              documentServerUrl={documentServerUrl}
              config={{
                document: {
                  fileType: "docx",
                  key: docSessionKey,
                  title: getFileNameFromUrl(docUrl),
                  url: docUrl?.replace(/^http:/, "https:") || "",
                  permissions: { edit: true, download: true },
                },
                editorConfig: {
                  user: {
                    id: user.uid,
                    name: user.displayName || user.email || "User",
                  },
                  customization: {
                    autosave: true,
                    forcesave: true,
                    uiTheme: "dark",
                  },
                },
                token,
                width: "100%",
                height: "100%",
              }}
              events={{
                onAppReady: function () {
                  console.log("[OO] Event: onAppReady fired.");
                  if (window.DocEditor && window.DocEditor.instances) {
                    const editorInstance =
                      window.DocEditor.instances["onlyoffice-editor"];
                    if (editorInstance) {
                      docEditorRef.current = editorInstance;
                      console.log(
                        "[OO] Event: onAppReady. Successfully set docEditorRef.current.",
                        docEditorRef.current
                      );
                    } else {
                      console.error(
                        "[OO] Event: onAppReady. window.DocEditor.instances['onlyoffice-editor'] is undefined."
                      );
                    }
                  } else {
                    console.error(
                      "[OO] Event: onAppReady. window.DocEditor or its instances are not available."
                    );
                  }
                },
                onDownloadAs,
              }}
            />
          </div>
        )}
      </>
    )}
  </div>

  {/* Save Dialog */}
  {showSaveDialog && (
    <div className="fixed inset-0 flex items-center justify-center z-50 bg-black/30">
      <div className="bg-white dark:bg-gray-900 rounded-lg shadow-lg p-6 min-w-[300px] flex flex-col items-center">
        <span className="text-2xl mb-2">✅</span>
        <div className="font-semibold mb-1">Document Saved!</div>
        <div className="text-xs text-muted-foreground mb-2 break-words">
          {savedFileName}
        </div>
        <div className="text-sm text-gray-500">Reloading...</div>
      </div>
    </div>
  )}
</div>

);
}

Dear @amritsundarka
Please create and send us a small sample of your project that includes the ForceSave scenario (the scenario that provokes issue), so we can deploy and test it (we don’t need the full project, just a simple sample).
The full code you provided is a bit difficult to troubleshoot, so we need a simpler sample for this scenario to check it out.
Additionally, please bear in mind, that you can contact colleagues of mine via Zendesk if you need complex analysis and get dedicated support:Get dedicated support

Here is the GitHub link for a sample project as you requested: GitHub - garg10aditya/onlyoffice-debug-app , the main problem is that we are getting docrefeditor as null, the document loads in the server and opens. Please go through this and let us know what we are doing wrong. We are unable to use the features of force save, live real time collaboration as well.

I have pulled your sample, but can’t find any configuration files to enable integration with my own Document server. Please double-check the project, we should be able to deploy it and reproduce the scenario from this thread.
If I misunderstood something, please clarify it.

I forgot to upload the default.json file for my OnlyOffice Document Server setup. Here’s how I set up the server using Docker

docker run -itd \
  -p 8080:80 \
  --restart=always \
  -v /opt/onlyoffice/DocumentServer/logs:/var/log/onlyoffice \
  -v /opt/onlyoffice/DocumentServer/data:/var/www/onlyoffice/Data \
  -v /opt/onlyoffice/DocumentServer/lib:/var/lib/onlyoffice \
  -v /opt/onlyoffice/DocumentServer/db:/var/lib/postgresql \
  -e JWT_SECRET=our_secret \
  --name onlyoffice-docs \
  onlyoffice/documentserver
{
        "statsd": {
                "useMetrics": false,
                "host": "localhost",
                "port": "8125",
                "prefix": "ds."
        },
        "externalUrl": "Our Server URL",
        "aiSettings": {
                "actions": {
                },
                "models": [
                ],
                "providers": {
                },
                "version": 3,
                "timeout": "30s",
                "allowedCorsOrigins": [
                        "https://onlyoffice.github.io", "https://onlyoffice-plugins.github.io"
                ],
                "pluginDir" : "../branding/info/ai"
        },
        "log": {
                "filePath": "",
                "options": {
                        "replaceConsole": true
                }
        },
        "runtimeConfig": {
                "filePath": "",
                "cache": {
                        "stdTTL": 300,
                        "checkperiod": 60,
                        "useClones": false
                }
        },
        "queue": {
                "type": "rabbitmq",
                "visibilityTimeout": 300,
                "retentionPeriod": 900
        },
        "email": {
                "smtpServerConfiguration": {
                        "host": "localhost",
                        "port": 587,
                        "auth": {
                                "user": "",
                                "pass": ""
                        }
                },
                "connectionConfiguration": {
                        "disableFileAccess": false,
                        "disableUrlAccess": false
                },
                "contactDefaults": {
                        "from": "from@example.com",
                        "to": "to@example.com"
                }
        },
        "notification": {
                "rules": {
                        "licenseExpirationWarning": {
                                "enable": false,
                                "transportType": [
                                        "email"
                                ],
                                "template": {
                                        "title": "%s Docs license expiration warning",
                                        "body": "Attention! Your license is about to expire on %s.\nUpon reaching this date, you will no longer be entitled to receive personal technical support and install new Docs versions released after this date."
                                },
                                "policies": {
                                        "repeatInterval": "1d"
                                }
                        },
                        "licenseExpirationError": {
                                "enable": false,
                                "transportType": [
                                        "email"
                                ],
                                "template": {
                                        "title": "%s Docs license expiration warning",
                                        "body": "Attention! Your license expired on %s.\nYou are no longer entitled to receive personal technical support and install new Docs versions released after this date.\nPlease contact sales@onlyoffice.com to discuss license renewal."
                                },
                                "policies": {
                                        "repeatInterval": "1d"
                                }
                        },
                        "licenseLimitEdit": {
                                "enable": false,
                                "transportType": [
                                        "email"
                                ],
                                "template": {
                                        "title": "%s Docs license connection limit warning",
                                        "body": "Attention! You have reached %s%% of the %s limit set by your license."
                                },
                                "policies": {
                                        "repeatInterval": "1h"
                                }
                        },
                        "licenseLimitLiveViewer": {
                                "enable": false,
                                "transportType": [
                                        "email"
                                ],
                                "template": {
                                        "title": "%s Docs license connection limit warning",
                                        "body": "Attention! You have reached %s%% of the live viewer %s limit set by your license."
                                },
                                "policies": {
                                        "repeatInterval": "1h"
                                }
                        }
                }
        },
        "storage": {
                "name": "storage-fs",
                "fs": {
                        "folderPath": "",
                        "urlExpires": 900,
                        "secretString": "verysecretstring"
                },
                "region": "",
                "endpoint": "http://localhost/s3",
                "bucketName": "cache",
                "storageFolderName": "files",
                "cacheFolderName": "data",
                "urlExpires": 604800,
                "accessKeyId": "",
                "secretAccessKey": "",
                "sslEnabled": false,
                "s3ForcePathStyle": true,
                "externalHost": "OUR SERVER URL",
                "useDirectStorageUrls": true
        },
        "persistentStorage": {
        },
        "rabbitmq": {
                "url": "amqp://localhost:5672",
                "socketOptions": {},
                "exchangepubsub": {
                        "name": "ds.pubsub",
                        "options": {
                                "durable": true
                        }
                },
                "queuepubsub": {
                        "name": "",
                        "options": {
                                "autoDelete": true,
                                "exclusive": true,
                                "arguments": {
                                        "x-queue-type": "classic"
                                }
                        }
                },
                "queueconverttask": {
                        "name": "ds.converttask6",
                        "options": {
                                "durable": true,
                                "maxPriority": 6,
                                "arguments": {
                                        "x-queue-type": "classic"
                                }
                        }
                },
                "queueconvertresponse": {
                        "name": "ds.convertresponse",
                        "options": {
                                "durable": true,
                                "arguments": {
                                        "x-queue-type": "classic"
                                }
                        }
                },
                "exchangeconvertdead": {
                        "name": "ds.exchangeconvertdead",
                        "options": {
                                "durable": true
                        }
                },
                "queueconvertdead": {
                        "name": "ds.convertdead",
                        "options": {
                                "durable": true,
                                "arguments": {
                                        "x-queue-type": "classic"
                                }
                        }
                },
                "queuedelayed": {
                        "name": "ds.delayed",
                        "options": {
                                "durable": true,
                                "arguments": {
                                        "x-queue-type": "classic"
                                }
                        }
                }
        },
        "activemq": {
                "connectOptions": {
                        "port": 5672,
                        "host": "localhost",
                        "reconnect": false
                },
                "queueconverttask": "ds.converttask",
                "queueconvertresponse": "ds.convertresponse",
                "queueconvertdead": "ActiveMQ.DLQ",
                "queuedelayed": "ds.delayed",
                "topicpubsub": "ds.pubsub"
        },
        "dnscache": {
                "enable" : true,
                "ttl" : 300,
                "cachesize" : 1000
        },
        "openpgpjs": {
                "config": {
                },
                "encrypt": {
                        "passwords": ["verysecretstring"]
                },
                "decrypt": {
                        "passwords": ["verysecretstring"]
                }
        },
        "aesEncrypt": {
                "config": {
                        "keyByteLength": 32,
                        "saltByteLength": 64,
                        "initializationVectorByteLength": 16,
                        "iterationsByteLength": 5
                },
                "secret": "verysecretstring"
        },
        "bottleneck": {
                "getChanges": {
                }
        },
        "win-ca": {
                "inject": "+"
        },
        "wopi": {
                "enable": false,
                "host" : "",
                "htmlTemplate" : "../../web-apps/apps/api/wopi",
                "wopiZone" : "external-http",
                "favIconUrlWord" : "/web-apps/apps/documenteditor/main/resources/img/favicon.ico",
                "favIconUrlCell" : "/web-apps/apps/spreadsheeteditor/main/resources/img/favicon.ico",
                "favIconUrlSlide" : "/web-apps/apps/presentationeditor/main/resources/img/favicon.ico",
                "favIconUrlPdf" : "/web-apps/apps/pdfeditor/main/resources/img/favicon.ico",
                "favIconUrlDiagram" : "/web-apps/apps/visioeditor/main/resources/img/favicon.ico",
                "fileInfoBlockList" : ["FileUrl"],
                "pdfView": ["djvu", "xps", "oxps"],
                "pdfEdit": ["pdf"],
                "forms": ["pdf"],
                "wordView": ["doc", "dotm", "dot", "fodt", "ott", "rtf", "mht", "mhtml", "html", "htm", "xml", "epub", "fb2", "sxw", "stw", "wps", "wpt", "pages", "docxf", "oform", "hwp", "hwpx", "md"],
                "wordEdit": ["docx", "dotx", "docm", "odt", "txt"],
                "cellView": ["xls", "xltm", "xlt", "fods", "ots", "sxc", "xml", "et", "ett", "numbers"],
                "cellEdit": ["xlsx", "xlsb", "xltx", "xlsm", "ods", "csv"],
                "slideView": ["ppt", "ppsx", "ppsm", "pps", "potm", "pot", "fodp", "otp", "sxi", "dps", "dpt", "key", "odg"],
                "slideEdit": ["pptx", "potx", "pptm", "odp"],
                "diagramView": ["vsdx", "vstx", "vssx", "vsdm", "vstm", "vssm"],
                "diagramEdit": [],
                "publicKey": "",
                "modulus": "",
                "exponent": 65537,
                "privateKey": "",
                "publicKeyOld": "",
                "modulusOld": "",
                "exponentOld": 65537,
                "privateKeyOld": "",
                "refreshLockInterval": "10m",
                "dummy" : {
                        "enable": false,
                        "sampleFilePath": ""
                }
        },
        "tenants": {
                "baseDir": "",
                "baseDomain": "",
                "filenameConfig": "config.json",
                "filenameSecret": "secret.key",
                "filenameLicense": "license.lic",
                "defaultTenant": "localhost",
                "cache" : {
                        "stdTTL": 300,
                        "checkperiod": 60,
                        "useClones": false
                }
        },
        "externalRequest": {
                "directIfIn" : {
                        "allowList": [],
                        "jwtToken": true
                },
                "action": {
                        "allow": true,
                        "blockPrivateIP": true,
                        "proxyUrl": "",
                        "proxyUser": {
                                "username": "",
                                "password": ""
                        },
                        "proxyHeaders": {
                        }
                }
        },
        "services": {
                "CoAuthoring": {
                        "server": {
                                "port": 8000,
                                "workerpercpu": 1,
                                "mode": "development",
                                "limits_tempfile_upload": 104857600,
                                "limits_image_size": 26214400,
                                "limits_image_download_timeout": {
                                        "connectionAndInactivity": "2m",
                                        "wholeCycle": "2m"
                                },
                                "callbackRequestTimeout": {
                                        "connectionAndInactivity": "10m",
                                        "wholeCycle": "10m"
                                },
                                "healthcheckfilepath": "../public/healthcheck.docx",
                                "savetimeoutdelay": 5000,
                                "edit_singleton": false,
                                "forgottenfiles": "forgotten",
                                "forgottenfilesname": "output",
                                "maxRequestChanges": 20000,
                                "openProtectedFile": true,
                                "isAnonymousSupport": true,
                                "editorDataStorage": "editorDataMemory",
                                "editorStatStorage": "",
                                "assemblyFormatAsOrigin": true,
                                "newFileTemplate" : "../../document-templates/new",
                                "downloadFileAllowExt": ["pdf", "xlsx"],
                                "tokenRequiredParams": true,
                                "forceSaveUsingButtonWithoutChanges": false
                        },
                        "requestDefaults": {
                                "headers": {
                                        "User-Agent": "Node.js/6.13",
                                        "Connection": "Keep-Alive"
                                },
                                "rejectUnauthorized": true
                        },
                        "autoAssembly": {
                                "enable": false,
                                "interval": "5m",
                                "step": "1m"
                        },
                        "utils": {
                                "utils_common_fontdir": "null",
                                "utils_fonts_search_patterns": "*.ttf;*.ttc;*.otf",
                                "limits_image_types_upload": "jpg;jpeg;jpe;png;gif;bmp;svg;tiff;tif"
                        },
                        "sql": {
                                "type": "postgres",
                                "tableChanges": "doc_changes",
                                "tableResult": "task_result",
                                "dbHost": "localhost",
                                "dbPort": 5432,
                                "dbName": "onlyoffice",
                                "dbUser": "onlyoffice",
                                "dbPass": "onlyoffice",
                                "charset": "utf8",
                                "connectionlimit": 10,
                                "max_allowed_packet": 1048575,
                                "pgPoolExtraOptions": {
                                        "idleTimeoutMillis": 30000,
                                        "maxLifetimeSeconds ": 60000,
                                        "statement_timeout ": 60000,
                                        "query_timeout  ": 60000,
                                        "connectionTimeoutMillis": 60000
                                },
                                "damengExtraOptions": {
                                        "columnNameUpperCase": false,
                                        "columnNameCase": "lower",
                                        "connectTimeout": 60000,
                                        "loginEncrypt": false,
                                        "localTimezone": 0,
                                        "poolTimeout": 60,
                                        "socketTimeout": 60000,
                                        "queueTimeout": 60000
                                },
                                "oracleExtraOptions": {
                                        "connectTimeout": 60
                                },
                                "msSqlExtraOptions": {
                                        "options": {
                                                "encrypt": false,
                                                "trustServerCertificate": true
                                        },
                                        "pool": {
                                                "idleTimeoutMillis": 30000
                                        }
                                },
                                "mysqlExtraOptions": {
                                        "connectTimeout": 60000,
                                        "queryTimeout": 60000
                                }
                        },
                        "redis": {
                                "name": "redis",
                                "prefix": "ds:",
                                "host": "127.0.0.1",
                                "port": 6379,
                                "options": {},
                                "optionsCluster": {},
                                "iooptions": {
                                        "lazyConnect": true
                                },
                                "iooptionsClusterNodes": [
                                ],
                                "iooptionsClusterOptions": {
                                        "lazyConnect": true
                                }
                        },
                        "pubsub": {
                                "maxChanges": 1000
                        },
                        "expire": {
                                "saveLock": 60,
                                "presence": 300,
                                "locks": 604800,
                                "changeindex": 86400,
                                "lockDoc": 30,
                                "message": 86400,
                                "lastsave": 604800,
                                "forcesave": 604800,
                                "forcesaveLock": 5000,
                                "saved": 3600,
                                "documentsCron": "0 */2 * * * *",
                                "files": 86400,
                                "filesCron": "00 00 */1 * * *",
                                "filesremovedatonce": 100,
                                "sessionidle": "1h",
                                "sessionabsolute": "30d",
                                "sessionclosecommand": "2m",
                                "pemStdTTL": "1h",
                                "pemCheckPeriod": "10m",
                                "updateVersionStatus": "5m",
                                "monthUniqueUsers": "1y"
                        },
                        "ipfilter": {
                                "rules": [{"address": "*", "allowed": true}],
                                "useforrequest": false,
                                "errorcode": 403
                        },
                        "request-filtering-agent" : {
                                "allowPrivateIPAddress": false,
                                "allowMetaIPAddress": false
                        },
                        "secret": {
                                "browser": {"string": "8b0e5356f4fc28204d461f72af45f26a", "file": ""},
                                "inbox": {"string": "8b0e5356f4fc28204d461f72af45f26a", "file": ""},
                                "outbox": {"string": "8b0e5356f4fc28204d461f72af45f26a", "file": ""},
                                "session": {"string": "8b0e5356f4fc28204d461f72af45f26a", "file": ""}
                        },
                        "token": {
                                "enable": {
                                        "browser": true,
                                        "request": {
                                                "inbox": true,
                                                "outbox": true
                                        }
                                },
                                "browser": {
                                        "secretFromInbox": true
                                },
                                "inbox": {
                                        "header": "Authorization",
                                        "prefix": "Bearer ",
                                        "inBody": false
                                },
                                "outbox": {
                                        "header": "Authorization",
                                        "prefix": "Bearer ",
                                        "algorithm": "HS256",
                                        "expires": "5m",
                                        "inBody": false,
                                        "urlExclusionRegex": ""
                                },
                                "session": {
                                        "algorithm": "HS256",
                                        "expires": "30d"
                                },
                                "verifyOptions": {
                                        "clockTolerance": 60
                                }
                        },
                        "plugins": {
                                "uri": "/sdkjs-plugins",
                                "autostart": []
                        },
                        "themes": {
                                "uri": "/web-apps/apps/common/main/resources/themes"
                        },
                        "editor":{
                                "spellcheckerUrl": "",
                                "reconnection":{
                                        "attempts": 50,
                                        "delay": "2s"
                                },
                                "binaryChanges": false,
                                "websocketMaxPayloadSize": "1.5MB",
                                "maxChangesSize": "150MB"
                        },
                        "sockjs": {
                                "sockjs_url": "",
                                "disable_cors": true,
                                "websocket": true
                        },
                        "socketio": {
                                "connection": {
                                        "path": "/doc/",
                                        "serveClient": false,
                                        "pingTimeout": 20000,
                                        "pingInterval": 25000,
                                        "maxHttpBufferSize": 1e8
                                }
                        },
                        "callbackBackoffOptions": {
                                "retries": 3,
                                "timeout":{
                                        "factor": 2,
                                        "minTimeout": 1000,
                                        "maxTimeout": 2147483647,
                                        "randomize": false
                                },
                                "httpStatus": "429,500-599"
                        }
                }
        },
        "license" : {
                "license_file": "",
                "warning_limit_percents": 70,
                "packageType": 0,
                "warning_license_expiration": "30d"
        },
        "FileConverter": {
                "converter": {
                        "maxDownloadBytes": 104857600,
                        "downloadTimeout": {
                                "connectionAndInactivity": "2m",
                                "wholeCycle": "2m"
                        },
                        "downloadAttemptMaxCount": 3,
                        "downloadAttemptDelay": 1000,
                        "maxprocesscount": 1,
                        "fontDir": "null",
                        "presentationThemesDir": "null",
                        "x2tPath": "null",
                        "docbuilderPath": "null",
                        "args": "",
                        "spawnOptions": {},
                        "errorfiles": "",
                        "streamWriterBufferSize": 8388608,
                        "maxRedeliveredCount": 2,
                        "inputLimits": [
                                {
                                "type": "docx;dotx;docm;dotm",
                                "zip": {
                                        "uncompressed": "50MB",
                                        "template": "*.xml"
                                }
                                },
                                {
                                "type": "xlsx;xltx;xlsm;xltm",
                                "zip": {
                                        "uncompressed": "300MB",
                                        "template": "*.xml"
                                }
                                },
                                {
                                "type": "pptx;ppsx;potx;pptm;ppsm;potm",
                                "zip": {
                                        "uncompressed": "50MB",
                                        "template": "*.xml"
                                }
                                }
                        ]
                }
        }
}

Hello @amritsundarka
I was able to deploy your project and integrate my own Document server through changing page.tsx config file:


However, it’s not enough to troubleshoot the situation since I just added simple file opening on my side, but we need the exact sample that provides the scenario issue you described in this thread.
Please provide us with information what changes we should implement in the project config files to reproduce the described scenario.

Thank you for your continued support and for taking the time to look at the sample project I provided.

To re-emphasize the core problem we are facing:

The Document Editor itself loads correctly for us. We are able to open and view the document within our Next.js application, which is great.

However, the primary issue is that we cannot programmatically interact with the editor instance after it has loaded. Specifically, we need to use the force save functionality. Our attempt to trigger a force save via a button click fails because our reference to the editor (docEditorRef.current) is null inside the button’s event handler.

We believe the docEditorRef being null is the root cause, as it prevents us from calling executeCommand(‘forcesave’, true). While the editor instance seems to exist (as it’s running), we are struggling to get a stable reference to it that persists across React re-renders and can be accessed by other functions in our component.

Could you please provide guidance or a best-practice example on how to correctly implement a “Force Save” button in a React/Next.js application? We need a reliable way to get the editor instance and call its API methods to save the current already edited document as .docx file in our POST API Server Endpoint.

Thank you again for your assistance.

Dear @amritsundarka
We understand your request, but we need a sample project where you implemented the Force Save scenario that failed. This way we will be able to check out entire scenario and figure out the situation. Unfortunately, we don’t have ready-to-go React.js sample with Force save feature for your reference, so we need a proper sample from your side to check out.

The provided earlier sample doesn’t contain Force Save scenario, so we cannot troubleshoot it.
Please provide us with proper sample or contact colleagues of mine to discuss possible options to speed up the process: Get dedicated support