Skip to content

diagrams-js

A TypeScript port of the diagrams Python library for drawing cloud system architecture diagrams as code

diagrams-js/custom-nodes

core
166 linesSource

Creating nodes with external icons using Custom() function. Support for remote URLs (browsers), local files (Node.js), and data URLs. Customize appearance with shape, width, height, margin options. Async icon loading with automatic injection into SVG output.

This skill builds on diagrams-js/getting-started. Read it first for foundational concepts.

diagrams-js — Custom Nodes

Create nodes with custom icons from URLs, local files, or data URLs. Useful for services not in standard providers or proprietary components.

Setup

ts
import { Diagram, Custom } from "diagrams-js";

const diagram = Diagram("Custom Services");

// Remote URL
const service = diagram.add(Custom("My Service", "https://example.com/icon.png"));

const svg = await diagram.render();

Core Patterns

Remote URL Icon

ts
import { Diagram, Custom } from "diagrams-js";

const diagram = Diagram("External Services");

const rabbitmq = diagram.add(Custom("Message Queue", "https://example.com/rabbitmq.png"));

const redis = diagram.add(Custom("Cache", "https://example.com/redis.png"));

rabbitmq.to(redis);

Local File Icon (Node.js only)

ts
import { Diagram, Custom } from "diagrams-js";

const diagram = Diagram("Local Services");

const service = diagram.add(Custom("Internal API", "./assets/internal-api.png"));

Data URL Icon

ts
import { Diagram, Custom } from "diagrams-js";

const diagram = Diagram("Embedded Icons");

const iconData = "data:image/png;base64,iVBORw0KGgo...";
const service = diagram.add(Custom("Service", iconData));

Custom Node Options

ts
const node = diagram.add(
  Custom("Service", "https://example.com/icon.png", {
    shape: "box",
    width: "1.2",
    height: "1.2",
    fixedsize: "true",
    margin: "0.1,0.1",
    labelloc: "b",
    imagescale: "true",
  }),
);

With Clusters

ts
const diagram = Diagram("Services");

const external = diagram.cluster("External Services");

const stripe = external.add(Custom("Stripe", "https://example.com/stripe.png"));

const twilio = external.add(Custom("Twilio", "https://example.com/twilio.png"));

stripe.to(twilio);

Common Mistakes

HIGH Using local file path in browser

Wrong:

ts
// In browser
const node = Custom("Service", "./local/icon.png");
// Fails: Browser cannot access local file system

Correct:

ts
// In browser - use remote URL or data URL
const node = Custom("Service", "https://example.com/icon.png");

// Or embed as data URL
const node = Custom("Service", "data:image/png;base64,...");

Browser cannot access local file system. Use remote URLs, data URLs, or load file server-side and pass data URL.

Source: docs/guides/custom-nodes.mdx

MEDIUM Not handling icon load failure

Wrong:

ts
const node = diagram.add(Custom("Service", "https://bad-url/icon.png"));
// Icon silently missing if fetch fails

Correct:

ts
// Check browser console for warnings
// Or pre-load icon and use data URL

async function loadIcon(url) {
  const response = await fetch(url);
  const blob = await response.blob();
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result);
    reader.readAsDataURL(blob);
  });
}

const iconData = await loadIcon("https://example.com/icon.png");
const node = diagram.add(Custom("Service", iconData));

Remote icon fetch can fail (CORS, 404, network). Check console warnings or pre-load as data URL.

Source: source code - Custom.ts _fetchRemoteIcon()

See also

  • diagrams-js/browser-integration — Browser-specific considerations
  • diagrams-js/nodejs-integration — Local file access in Node.js