Collaboration

Two users editing the same spreadsheet in real time. Both grids below are connected to the same document on Jspreadsheet Server: a change in one appears in the other, and in every browser that has this page open. Teams work together on one spreadsheet, like Google Sheets, embedded in your own application.

<!DOCTYPE html>
<html lang="pt-BR">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jspreadsheet@13/dist/jspreadsheet.min.css" type="text/css" />
<link rel="stylesheet" href="https://jsuites.net/v6/jsuites.css" type="text/css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Material+Icons&display=swap" />
<script src="https://jsuites.net/v6/jsuites.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jspreadsheet@13/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lemonadejs/dist/lemonade.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@lemonadejs/studio/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@jspreadsheet/client/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/socket.io@4.7.5/client-dist/socket.io.min.js"></script>

<style>
.collab { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; padding: 20px; background: #f6f7fb; font-family: Inter, -apple-system, "Segoe UI", Roboto, sans-serif; }
.collab-panel { min-width: 0; background: #fff; border: 1px solid #e5e7eb; box-shadow: 0 4px 14px rgba(17, 24, 39, .05); }
.collab-head { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-bottom: 1px solid #e5e7eb; font-size: 13px; color: #1f2937; }
.collab-head b { font-weight: 600; }
.collab-head span { color: #6b7280; margin-left: auto; font-size: 12px; }
.collab-avatar { width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; color: #fff; font-weight: 600; font-size: 12px; }
.collab-sheet { padding: 12px; overflow-x: auto; }
.collab-sheet .jss_container { vertical-align: top; }
.collab-hint { text-align: center; font-size: 13px; color: #6b7280; margin: 14px 0 0; }
@media (max-width: 900px) { .collab { grid-template-columns: 1fr; } }
</style>

<div class="collab">
  <div class="collab-panel">
    <div class="collab-head">
      <div class="collab-avatar" style="background: #2563eb">A</div>
      <b>Alice</b><span>Browser 1</span>
    </div>
    <div class="collab-sheet"><div id="sheetA"></div></div>
  </div>
  <div class="collab-panel">
    <div class="collab-head">
      <div class="collab-avatar" style="background: #ea580c">B</div>
      <b>Bob</b><span>Browser 2</span>
    </div>
    <div class="collab-sheet"><div id="sheetB"></div></div>
  </div>
</div>

<p class="collab-hint">
  Type in either sheet: the change travels through
  <a href="/products/server">Jspreadsheet Server</a> and appears in the other one, and in every
  browser that has this page open.
</p>

<script>
  (function () {
    const GUID = '4b9e6f12-8c3a-4d57-b1e0-6a2f9c8d7e31';
    const SERVER_URL = 'https://jspreadsheet.com';
    const SERVER_PATH = 's/';

    let remote;
    let instances = [];

    function removeImageButton(toolbarOptions) {
      for (let i = 0; i < toolbarOptions.items.length; i++) {
        if (toolbarOptions.items[i].content === 'add_photo_alternate') {
          toolbarOptions.items.splice(i, 1);
          i--;
        }
      }
      return toolbarOptions;
    }

    function init() {
      if (window.jspreadsheet && typeof jspreadsheet.destroyAll === 'function') {
        jspreadsheet.destroyAll();
      }

      // Set your JSS license key (The following key only works for one day)
      jspreadsheet.setLicense('M2Q1MmFiODIzMTJjZjVhY2I5ZTBlMDA3MmJhNzQ0YzlhNDUwZjAyYjcwNjc2MTZlYWU3ZTAwOGJjNjQ1NTBlNTJlNjU2NzZjNjMxNjQ3MGRiMjkyZmFhYzFjZjVkOTY2MDg0MTExZThiZTU0NWRjZTU2MWVlZWM5MDdmZjMyNDIsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpreE56QTFPVGs0TENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW5ZeE15SXNJbU5vWVhKMGN5SXNJbVp2Y20xeklpd2labTl5YlhWc1lTSXNJbkJoY25ObGNpSXNJbkpsYm1SbGNpSXNJbU52YlcxbGJuUnpJaXdpYVcxd2IzSjBaWElpTENKaVlYSWlMQ0oyWVd4cFpHRjBhVzl1Y3lJc0luTmxZWEpqYUNJc0luQnlhVzUwSWl3aWMyaGxaWFJ6SWl3aVkyeHBaVzUwSWl3aWMyVnlkbVZ5SWl3aWMyaGhjR1Z6SWl3aVptOXliV0YwSWl3aWNHbDJiM1FpWFN3aVpHVnRieUk2ZEhKMVpYMD0=');

      jspreadsheet.setExtensions({ formula, client });

      remote = client.connect({
        url: SERVER_URL,
        path: SERVER_PATH,
      });

      // Created by the first visitor; afterwards the document already exists
      const created = remote.create(GUID, {
        tabs: false,
        toolbar: true,
        worksheets: [{
          worksheetName: 'Sprint',
          minDimensions: [6, 12],
          columns: [
            { title: 'Task', width: 170 },
            { title: 'Owner', width: 90 },
            { title: 'Status', width: 100, type: 'dropdown', source: ['To do', 'Doing', 'Done'] },
            { title: 'Due', width: 100, type: 'calendar', options: { format: 'DD/MM/YYYY' } },
            { title: 'Hours', width: 70, type: 'numeric' },
            { title: 'Done', width: 60, type: 'checkbox' },
          ],
          data: [
            ['Landing page copy', 'Alice', 'Doing', '2026-10-12', 6, false],
            ['Pricing table', 'Bob', 'To do', '2026-10-14', 4, false],
            ['Signup flow', 'Alice', 'Done', '2026-10-08', 8, true],
            ['Email templates', 'Bob', 'Doing', '2026-10-15', 5, false],
            ['Analytics events', 'Alice', 'To do', '2026-10-18', 3, false],
            ['Total', '', '', '', '=SUM(E1:E5)', ''],
          ],
          style: { A6: 'font-weight: bold', E6: 'font-weight: bold' },
        }],
      });

      const commonOptions = {
        guid: GUID,
        toolbar: removeImageButton,
        onbeforeloadimage: function () {
          return '/templates/default/img/blocked.png';
        },
      };

      const elA = document.getElementById('sheetA');
      const elB = document.getElementById('sheetB');

      // Open both grids once the document exists
      Promise.resolve(created).catch(function() {}).then(function() {
        instances.push(jspreadsheet(elA, commonOptions));
        instances.push(jspreadsheet(elB, commonOptions));
      });
    }

    function cleanup() {
      if (window.jspreadsheet && typeof jspreadsheet.destroyAll === 'function') {
        jspreadsheet.destroyAll();
      }
      instances = [];
    }

    init();
    window.addEventListener('beforeunload', cleanup);
  })();
</script>
</html>
import React, { useRef, useEffect } from 'react';
import { Spreadsheet, jspreadsheet } from '@jspreadsheet/react';
import formula from '@jspreadsheet/formula-pro';
import client from '@jspreadsheet/client';

export default function JSServerMirror() {
  const GUID = "4b9e6f12-8c3a-4d57-b1e0-6a2f9c8d7e31";
  const SERVER_URL = "https://jspreadsheet.com";
  const SERVER_PATH = "s/";

  const maxParentWidth = 1300;
  const sheetMaxWidth = 600; 
  const sheetHeight = 300; 

  const sheetARef = useRef(null);
  const sheetBRef = useRef(null);

  useEffect(() => {
    if (!window.jspreadsheet) {
      console.error("jspreadsheet is not available on window.");
      return;
    }

    const { jspreadsheet } = window;
    const { formula, client } = window;

    if (typeof jspreadsheet.destroyAll === "function") {
      jspreadsheet.destroyAll();
    }

    // Set your JSS license key (The following key only works for one day)
    jspreadsheet.setLicense('M2Q1MmFiODIzMTJjZjVhY2I5ZTBlMDA3MmJhNzQ0YzlhNDUwZjAyYjcwNjc2MTZlYWU3ZTAwOGJjNjQ1NTBlNTJlNjU2NzZjNjMxNjQ3MGRiMjkyZmFhYzFjZjVkOTY2MDg0MTExZThiZTU0NWRjZTU2MWVlZWM5MDdmZjMyNDIsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpreE56QTFPVGs0TENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW5ZeE15SXNJbU5vWVhKMGN5SXNJbVp2Y20xeklpd2labTl5YlhWc1lTSXNJbkJoY25ObGNpSXNJbkpsYm1SbGNpSXNJbU52YlcxbGJuUnpJaXdpYVcxd2IzSjBaWElpTENKaVlYSWlMQ0oyWVd4cFpHRjBhVzl1Y3lJc0luTmxZWEpqYUNJc0luQnlhVzUwSWl3aWMyaGxaWFJ6SWl3aVkyeHBaVzUwSWl3aWMyVnlkbVZ5SWl3aWMyaGhjR1Z6SWl3aVptOXliV0YwSWl3aWNHbDJiM1FpWFN3aVpHVnRieUk2ZEhKMVpYMD0=');

    if (typeof jspreadsheet.setExtensions === "function") {
      jspreadsheet.setExtensions({ formula, client });
    }

    const remote = client.connect({ url: SERVER_URL, path: SERVER_PATH });

    remote.create(GUID, {
      tabs: false,
      toolbar: true,
      worksheets: [
        {
          minDimensions: [4, 6],
          worksheetName: "Sheet 1",
        },
      ],
    });

    function stripImageButton(toolbarOptions) {
      for (let i = 0; i < toolbarOptions.items.length; i++) {
        if (toolbarOptions.items[i].content === "add_photo_alternate") {
          toolbarOptions.items.splice(i, 1);
          i--;
        }
      }
      return toolbarOptions;
    }

    const commonOptions = {
      guid: GUID,
      toolbar: stripImageButton,
      onbeforeloadimage: function () {
        return "/templates/default/img/blocked.png";
      },
    };

    const instA = jspreadsheet(sheetARef.current, commonOptions);
    const instB = jspreadsheet(sheetBRef.current, commonOptions);

    return () => {
      try {
        if (instA && typeof instA.destroy === "function") instA.destroy();
        if (instB && typeof instB.destroy === "function") instB.destroy();
      } catch (e) {
        if (typeof jspreadsheet.destroyAll === "function") {
          jspreadsheet.destroyAll();
        }
      }
    };
  }, []);

  return (
    <div>
      <div
        className="row middle"
        style={{
          justifyContent: "center",
          maxWidth: `${maxParentWidth}px`,
          margin: "0 auto",
        }}
      >
        <div className="column p10">
          <div
            id="sheetA"
            ref={sheetARef}
            style={{
              maxWidth: `${sheetMaxWidth}px`,
              height: `${sheetHeight}px`,
              boxSizing: "border-box",
            }}
          />
        </div>
        <div className="column p10">
          <div
            id="sheetB"
            ref={sheetBRef}
            style={{
              maxWidth: `${sheetMaxWidth}px`,
              height: `${sheetHeight}px`,
              boxSizing: "border-box",
            }}
          />
        </div>
      </div>

      <p style={{ textAlign: "center" }}>
        Click here to find more information about the{" "}
        <a href="/products/server">Jspreadsheet Server</a> extension
      </p>
    </div>
  );
}
<template>
  <div class="row middle" style="justify-content:center; max-width:1300px; margin:0 auto;">
    <div class="column p10">
      <Spreadsheet
        ref="sheetA"
        :license="license"
        :guid="GUID"
        :toolbar="stripImageButton"
        :onbeforeloadimage="onBeforeLoadImage"
        :tableOverflow="true"
        :tableWidth="1300"
        :tableHeight="500"
      />
    </div>

    <div class="column p10" style="margin-top:10px;">
      <Spreadsheet
        ref="sheetB"
        :license="license"
        :guid="GUID"
        :toolbar="stripImageButton"
        :onbeforeloadimage="onBeforeLoadImage"
        :tableOverflow="true"
        :tableWidth="1300"
        :tableHeight="500"
      />
    </div>

    <p style="text-align:center; width:100%; margin-top:10px;">
      Click here to find more information about the
      <a href="/products/server">Jspreadsheet Server</a> extension
    </p>
  </div>
</template>

<script>
import { Spreadsheet, jspreadsheet } from "@jspreadsheet/vue";
import formula from '@jspreadsheet/formula-pro';
import client from '@jspreadsheet/client';
import "jspreadsheet/dist/jspreadsheet.css";
import "jsuites/dist/jsuites.css";

// Set your JSS license key (The following key only works for one day)
jspreadsheet.setLicense('M2Q1MmFiODIzMTJjZjVhY2I5ZTBlMDA3MmJhNzQ0YzlhNDUwZjAyYjcwNjc2MTZlYWU3ZTAwOGJjNjQ1NTBlNTJlNjU2NzZjNjMxNjQ3MGRiMjkyZmFhYzFjZjVkOTY2MDg0MTExZThiZTU0NWRjZTU2MWVlZWM5MDdmZjMyNDIsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpreE56QTFPVGs0TENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW5ZeE15SXNJbU5vWVhKMGN5SXNJbVp2Y20xeklpd2labTl5YlhWc1lTSXNJbkJoY25ObGNpSXNJbkpsYm1SbGNpSXNJbU52YlcxbGJuUnpJaXdpYVcxd2IzSjBaWElpTENKaVlYSWlMQ0oyWVd4cFpHRjBhVzl1Y3lJc0luTmxZWEpqYUNJc0luQnlhVzUwSWl3aWMyaGxaWFJ6SWl3aVkyeHBaVzUwSWl3aWMyVnlkbVZ5SWl3aWMyaGhjR1Z6SWl3aVptOXliV0YwSWl3aWNHbDJiM1FpWFN3aVpHVnRieUk2ZEhKMVpYMD0=');
jspreadsheet.setExtensions({ formula, client });

export default {
  name: "JSServerMirrorVue",
  components: { Spreadsheet },
  data() {
    return {
      // Set your JSS license key (The following key only works for one day)
      license: 'M2Q1MmFiODIzMTJjZjVhY2I5ZTBlMDA3MmJhNzQ0YzlhNDUwZjAyYjcwNjc2MTZlYWU3ZTAwOGJjNjQ1NTBlNTJlNjU2NzZjNjMxNjQ3MGRiMjkyZmFhYzFjZjVkOTY2MDg0MTExZThiZTU0NWRjZTU2MWVlZWM5MDdmZjMyNDIsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpreE56QTFPVGs0TENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW5ZeE15SXNJbU5vWVhKMGN5SXNJbVp2Y20xeklpd2labTl5YlhWc1lTSXNJbkJoY25ObGNpSXNJbkpsYm1SbGNpSXNJbU52YlcxbGJuUnpJaXdpYVcxd2IzSjBaWElpTENKaVlYSWlMQ0oyWVd4cFpHRjBhVzl1Y3lJc0luTmxZWEpqYUNJc0luQnlhVzUwSWl3aWMyaGxaWFJ6SWl3aVkyeHBaVzUwSWl3aWMyVnlkbVZ5SWl3aWMyaGhjR1Z6SWl3aVptOXliV0YwSWl3aWNHbDJiM1FpWFN3aVpHVnRieUk2ZEhKMVpYMD0=',
      GUID: "4b9e6f12-8c3a-4d57-b1e0-6a2f9c8d7e31",
      SERVER_URL: "https://jspreadsheet.com",
      SERVER_PATH: "s/",
    };
  },
  created() {
    const { jspreadsheet, formula, client } = window;

    if (!jspreadsheet) {
      console.error("jspreadsheet not found.");
      return;
    }

    if (client && typeof client.connect === "function") {
      const remote = client.connect({
        url: this.SERVER_URL,
        path: this.SERVER_PATH,
      });

      remote.create(this.GUID, {
        tabs: false,
        toolbar: true,
        worksheets: [
          {
            minDimensions: [4, 6],
            worksheetName: "Sheet 1",
          },
        ],
      });
    }
  },
  methods: {
    stripImageButton(toolbarOptions) {
      if (!toolbarOptions || !Array.isArray(toolbarOptions.items)) return toolbarOptions;
      for (let i = 0; i < toolbarOptions.items.length; i++) {
        if (toolbarOptions.items[i].content === "add_photo_alternate") {
          toolbarOptions.items.splice(i, 1);
          i--;
        }
      }
      return toolbarOptions;
    },
    onBeforeLoadImage() {
      return "/templates/default/img/blocked.png";
    },
  },
  beforeUnmount() {
    const { jspreadsheet } = window;
    if (jspreadsheet && typeof jspreadsheet.destroyAll === "function") {
      jspreadsheet.destroyAll();
    }
  },
};
</script>
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Spreadsheet } from '@jspreadsheet/angular';
import { jspreadsheet } from 'jspreadsheet';
import formula from '@jspreadsheet/formula-pro';
import client from '@jspreadsheet/client';

// Set your JSS license key (The following key only works for one day)
jspreadsheet.setLicense('M2Q1MmFiODIzMTJjZjVhY2I5ZTBlMDA3MmJhNzQ0YzlhNDUwZjAyYjcwNjc2MTZlYWU3ZTAwOGJjNjQ1NTBlNTJlNjU2NzZjNjMxNjQ3MGRiMjkyZmFhYzFjZjVkOTY2MDg0MTExZThiZTU0NWRjZTU2MWVlZWM5MDdmZjMyNDIsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpreE56QTFPVGs0TENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW5ZeE15SXNJbU5vWVhKMGN5SXNJbVp2Y20xeklpd2labTl5YlhWc1lTSXNJbkJoY25ObGNpSXNJbkpsYm1SbGNpSXNJbU52YlcxbGJuUnpJaXdpYVcxd2IzSjBaWElpTENKaVlYSWlMQ0oyWVd4cFpHRjBhVzl1Y3lJc0luTmxZWEpqYUNJc0luQnlhVzUwSWl3aWMyaGxaWFJ6SWl3aVkyeHBaVzUwSWl3aWMyVnlkbVZ5SWl3aWMyaGhjR1Z6SWl3aVptOXliV0YwSWl3aWNHbDJiM1FpWFN3aVpHVnRieUk2ZEhKMVpYMD0=');
jspreadsheet.setExtensions({ formula, client });

declare global {
  interface Window {
    jspreadsheet: any;
    formula: any;
    client: any;
  }
}

@Component({
  selector: 'app-jss-server-mirror',
  standalone: true,
  imports: [Spreadsheet],
  template: `
    <div class="row middle"
         style="justify-content:center; max-width:1300px; margin:0 auto;">
      <div class="column p10">
        <Spreadsheet
          [license]="license"
          [guid]="GUID"
          [toolbar]="stripImageButton"
          [onbeforeloadimage]="onBeforeLoadImage"
          [tableOverflow]="true"
          [tableWidth]="1300"
          [tableHeight]="500">
        </Spreadsheet>
      </div>

      <div class="column p10" style="margin-top:10px;">
        <Spreadsheet
          [license]="license"
          [guid]="GUID"
          [toolbar]="stripImageButton"
          [onbeforeloadimage]="onBeforeLoadImage"
          [tableOverflow]="true"
          [tableWidth]="1300"
          [tableHeight]="500">
        </Spreadsheet>
      </div>

      <p style="text-align:center; width:100%; margin-top:10px;">
        Click here to find more information about the
        <a href="/products/server">Jspreadsheet Server</a> extension
      </p>
    </div>
  `
})
export class JssServerMirrorComponent implements OnInit, OnDestroy {
  // Set your JSS license key (The following key only works for one day)
  license = 'M2Q1MmFiODIzMTJjZjVhY2I5ZTBlMDA3MmJhNzQ0YzlhNDUwZjAyYjcwNjc2MTZlYWU3ZTAwOGJjNjQ1NTBlNTJlNjU2NzZjNjMxNjQ3MGRiMjkyZmFhYzFjZjVkOTY2MDg0MTExZThiZTU0NWRjZTU2MWVlZWM5MDdmZjMyNDIsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpreE56QTFPVGs0TENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW5ZeE15SXNJbU5vWVhKMGN5SXNJbVp2Y20xeklpd2labTl5YlhWc1lTSXNJbkJoY25ObGNpSXNJbkpsYm1SbGNpSXNJbU52YlcxbGJuUnpJaXdpYVcxd2IzSjBaWElpTENKaVlYSWlMQ0oyWVd4cFpHRjBhVzl1Y3lJc0luTmxZWEpqYUNJc0luQnlhVzUwSWl3aWMyaGxaWFJ6SWl3aVkyeHBaVzUwSWl3aWMyVnlkbVZ5SWl3aWMyaGhjR1Z6SWl3aVptOXliV0YwSWl3aWNHbDJiM1FpWFN3aVpHVnRieUk2ZEhKMVpYMD0=';

  GUID = '4b9e6f12-8c3a-4d57-b1e0-6a2f9c8d7e31';
  SERVER_URL = 'https://jspreadsheet.com';
  SERVER_PATH = 's/';

  ngOnInit(): void {
    const { jspreadsheet, formula, client } = window;

    if (!jspreadsheet) {
      console.error('jspreadsheet not found.');
      return;
    }

    if (typeof jspreadsheet.destroyAll === 'function') {
      jspreadsheet.destroyAll();
    }

    if (client && typeof client.connect === 'function') {
      const remote = client.connect({
        url: this.SERVER_URL,
        path: this.SERVER_PATH,
      });

      remote.create(this.GUID, {
        tabs: false,
        toolbar: true,
        worksheets: [
          {
            minDimensions: [4, 6],
            worksheetName: 'Sheet 1',
          },
        ],
      });
    }
  }

  ngOnDestroy(): void {
    const { jspreadsheet } = window;
    if (jspreadsheet && typeof jspreadsheet.destroyAll === 'function') {
      jspreadsheet.destroyAll();
    }
  }

  stripImageButton = (toolbarOptions: any) => {
    if (!toolbarOptions || !Array.isArray(toolbarOptions.items)) return toolbarOptions;
    for (let i = 0; i < toolbarOptions.items.length; i++) {
      if (toolbarOptions.items[i].content === 'add_photo_alternate') {
        toolbarOptions.items.splice(i, 1);
        i--;
      }
    }
    return toolbarOptions;
  };

  onBeforeLoadImage = () => {
    return '/templates/default/img/blocked.png';
  };
}

How it works

  • client.connect({ url, path }) opens one realtime connection to Jspreadsheet Server.
  • remote.create(guid, config) creates the document the first time; afterwards it already exists and the call does nothing.
  • Every spreadsheet created with the same guid loads that document from the server and stays in sync with it: edits, formulas, styles and worksheets.
  • The server persists each change through your adapter and authorizes every request through your hooks. See Jspreadsheet Server.

For a complete application built on the same server, see the Intrasheets demo.