> ## Documentation Index
> Fetch the complete documentation index at: https://www.towbar.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Excalidraw

> Excalidraw is a browser-based whiteboard for sketches and diagrams.

export const UseCaseNavigation = () => {
  useLayoutEffect(() => {
    const storageKey = "towbar-use-case-open-groups";
    const groupSelector = "#sidebar li[data-title] > button[aria-expanded]";
    let restoring = false;
    let frame = 0;
    function storedGroups() {
      try {
        return new Set(JSON.parse(sessionStorage.getItem(storageKey) || "[]"));
      } catch {
        return new Set();
      }
    }
    function save(groups) {
      try {
        sessionStorage.setItem(storageKey, JSON.stringify([...groups]));
      } catch {}
    }
    function groupName(button) {
      return button.parentElement?.dataset.title;
    }
    function rememberOpenGroups() {
      const groups = new Set();
      for (const button of document.querySelectorAll(groupSelector)) {
        if (button.getAttribute("aria-expanded") === "true") {
          const name = groupName(button);
          if (name) groups.add(name);
        }
      }
      save(groups);
    }
    function restoreOpenGroups() {
      frame = 0;
      const groups = storedGroups();
      if (!groups.size) return;
      restoring = true;
      for (const button of document.querySelectorAll(groupSelector)) {
        if (groups.has(groupName(button)) && button.getAttribute("aria-expanded") === "false") {
          button.click();
        }
      }
      restoring = false;
    }
    function scheduleRestore() {
      if (!frame) frame = requestAnimationFrame(restoreOpenGroups);
    }
    function onClick(event) {
      if (restoring || !(event.target instanceof Element)) return;
      const button = event.target.closest(groupSelector);
      if (button) {
        const name = groupName(button);
        if (!name) return;
        const groups = storedGroups();
        if (button.getAttribute("aria-expanded") === "true") groups.delete(name); else groups.add(name);
        save(groups);
        return;
      }
      if (event.target.closest('a[href^="/docs/use-cases/"]')) {
        rememberOpenGroups();
      }
    }
    const observer = new MutationObserver(scheduleRestore);
    document.addEventListener("click", onClick, true);
    observer.observe(document.getElementById("sidebar") || document.body, {
      childList: true,
      subtree: true
    });
    scheduleRestore();
    return () => {
      document.removeEventListener("click", onClick, true);
      observer.disconnect();
      cancelAnimationFrame(frame);
    };
  }, []);
  return null;
};

<UseCaseNavigation />

<img className="towbar-doc-brand-logo" src="https://mintcdn.com/avgeek/EGkEWLTQnOwecqNx/assets/use-case-logos/excalidraw.svg?fit=max&auto=format&n=EGkEWLTQnOwecqNx&q=85&s=de5a89eb0954765a9dff608133158bad" alt="Excalidraw logo" aria-hidden="true" width="24" height="24" data-path="assets/use-case-logos/excalidraw.svg" />

**Upstream source:** [Current installation guide](https://github.com/excalidraw/excalidraw/blob/master/dev-docs/docs/introduction/development.mdx).

The official image serves only Excalidraw's browser client. It needs no Datastore or Service volume, but the self-hosted client does not provide Excalidraw's sharing and live collaboration services.

## Towbar manifest

```yaml title=".towbar/services/excalidraw.service.yml" theme={"system"}
id: excalidraw
name: Excalidraw
buildServer: null
deployment:
  type: image
  image: excalidraw/excalidraw@sha256:f7ee194addd607bf831d2af0f0a34463dd4225e426cf35199ef0b12a803398e9
container:
  port: 80
environments:
  production:
    server: 192.0.2.10
domains:
  primary: excalidraw.example.com
tls:
  mode: direct
```

## Configure

1. Prepare the example server, connect the repository, and map `production` to the branch containing these manifests. Replace the example server IP and domain. Commit the manifests, then sync the repository and inspect the resolved configuration.
2. Deploy the Service, then perform the checks below before enabling auto-deploy.

## Verify

Create a diagram and export it. Test in another browser before assuming drawings or collaboration are shared across users; this recipe deploys only the client.

For field constraints, see [Service manifest](/docs/services/manifest).
