AykutSarac/jsoncrack.com · error

Canvas not found.

Error message

Canvas not found.

What it means

Toast from clipboardImage() when getExportElement() returns null. getExportElement queries the DOM for `.jsoncrack-canvas` first, falling back to `svg[id*='ref']`. If neither exists (the graph canvas was not rendered, was unmounted, or uses a different selector in an integration), the clipboard copy aborts.

Source

Thrown at apps/www/src/features/modals/DownloadModal/index.tsx:83

const getExportElement = () =>
  (document.querySelector(".jsoncrack-canvas") as HTMLElement | null) ??
  (document.querySelector("svg[id*='ref']") as HTMLElement | null);

export const DownloadModal = ({ opened, onClose }: ModalProps) => {
  const [extension, setExtension] = React.useState(Extensions.PNG);
  const [fileDetails, setFileDetails] = React.useState({
    filename: "jsoncrack.com",
    backgroundColor: "#FFFFFF",
    quality: 1,
  });

  const clipboardImage = async () => {
    try {
      toast.loading("Copying to clipboard...", { id: "toastClipboard" });

      const imageElement = getExportElement();
      if (!imageElement) {
        toast.error("Canvas not found.");
        return;
      }
      const imageOptions = {
        quality: fileDetails.quality,
        backgroundColor: fileDetails.backgroundColor,
        skipFonts: true,
      };

      const blob = await toBlob(imageElement, imageOptions);

      if (!blob) return;

      await navigator.clipboard?.write([
        new ClipboardItem({
          [blob.type]: blob,
        }),
      ]);

View on GitHub (pinned to 3c9af69e23)

Solutions

  1. Ensure the graph has rendered before enabling the Clipboard button (gate on a non-empty canvas).
  2. Confirm the canvas wrapper retains the `jsoncrack-canvas` class.
  3. Wait for layout/onLayoutChange before allowing export.
  4. If integrating, render the component so that `.jsoncrack-canvas` or an `svg[id*='ref']` exists in the DOM.

Example fix

// before
const imageElement = getExportElement();
if (!imageElement) {
  toast.error("Canvas not found.");
  return;
}

// after — retry once after a frame to tolerate not-yet-painted canvas
const waitFrame = () => new Promise(r => requestAnimationFrame(() => r(null)));
let imageElement = getExportElement();
if (!imageElement) { await waitFrame(); imageElement = getExportElement(); }
if (!imageElement) {
  toast.error("Canvas not found. Wait for the graph to render first.");
  return;
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure the export target exists in the DOM before enabling Clipboard
export function canvasReady(): boolean {
  return !!(document.querySelector(".jsoncrack-canvas") || document.querySelector("svg[id*='ref']"));
}

Type guard

// Treat getExportElement's result as a present HTMLElement
function isExportElement(el: Element | null): el is HTMLElement {
  return el instanceof HTMLElement;
}

Try / catch

// Guard, then act; offer a user-actionable hint
const el = getExportElement();
if (!el) {
  toast.error("Canvas not found. Wait for the graph to render.");
  return;
}

Prevention

When it happens

Trigger: Opening the Download modal before the graph has rendered; the canvas unmounted due to an error/empty input; embedding JSONCrack with a custom className that replaced `.jsoncrack-canvas`; an integration that does not render the reaflow Canvas.

Common situations: Clicking 'Clipboard' immediately on modal open before layout completes; calling export on an empty/above-limit graph (no canvas rendered); DOM stripped by an error boundary.

Related errors


AI-assisted analysis of AykutSarac/jsoncrack.com@3c9af69e23 (2026-08-12). Data as JSON: /api/errors/090b26363b02dad8. Report an issue: GitHub.