Skip to content
FluxGantt

Import & Export

FluxGantt reads and writes its data as JSON and CSV, and exports the rendered chart as SVG or PNG. All of it lives behind the opt-in withIo mixin, so a consumer who never imports or exports never downloads the parsers and serializers:

import { createGantt } from '@fluxgantt/core';
import { withIo } from '@fluxgantt/core/io';
 
const gantt = withIo(createGantt({ tasks: [/* … */] }));

Compose it before you call any of the methods below. (exportSvg/exportPng additionally need withRender and a live mount().)

Export JSON / CSV

These are read-only and work headless (no mount required):

const bundle = gantt.exportJson();   // ExportBundle: { fluxgantt, tasks, dependencies, ... }
const csv = gantt.exportCsv();        // string

Both accept an options object (e.g. a timezone; it defaults to the instance's own calendar timezone). After destroy(), they return an empty-but-valid result rather than throwing.

Export SVG / PNG

These serialize the currently-mounted chart, so they require a prior mount():

const svg = gantt.exportSvg();              // string — a self-contained <svg> document
const blob = await gantt.exportPng();       // Promise<Blob> — rasterized PNG
  • exportSvg() bakes computed styles inline and adds the XML declaration + xmlns, so the string is a standalone SVG file.
  • Both exports paint a ground behind the chart: the chart's resolved --fg-bg (dark theme #0a0a0a), else #ffffff. Pass { background: '#fafafa' } (a hex, rgb()/hsl() or keyword) to override, or { background: 'transparent' } to leave it out (an invalid value throws / rejects). exportSvg() adds it as a <rect> in the exported copy only.
  • exportPng() is async (it waits for the browser to decode the SVG, or in Canvas mode for the Canvas export module to load, before rasterizing) and returns a Blob. Both throw / reject if the instance was never mounted (there is no sensible empty result for a picture of nothing).

Exporting a large (Canvas-mode) chart

Both methods also work above the 2000-task auto-switch threshold, when the chart is drawn on a canvas. Wait for renderer:selected first: until the Canvas module has loaded the instance is not yet mounted and the call throws the usual "not mounted" error. The export always contains the whole chart (every row, collapsed subtrees omitted), not just the scrolled-into-view window, and never the selection or focus ring.

  • Prefer exportSvg() for big charts. It is vector and has no pixel limit. It rebuilds the chart off-screen in one synchronous call, so it blocks the main thread for roughly a second per 2,000 rows, and it throws above 5,000 visible rows (collapsed descendants are not counted, so collapsing rows gets a larger plan under the cap). Measured main-thread block at 5,000 rows: about 2 s with native Temporal and about 6 s with @js-temporal/polyfill (about 1 s and 3 s at 2,100 rows). It is synchronous, so the page is unresponsive meanwhile; trigger it from an explicit user action. exportSvg({ inlineComputedStyle: false }) is a little faster.
  • exportPng() paints the full chart into one canvas, so the PNG is poster-sized: about 2,047 rows at the default density is already 65,535 px tall. It rejects with an actionable error past the browser's canvas limits (65,535 px per side; a total of ~134 million pixels, which is 512 MiB of memory; far less on Safari, where about 2,000 rows needs scale near 0.4). Lower scale, use a more compact density, collapse rows, or use exportSvg(). The same 5,000-visible-row cap applies.
  • SVG-mode exportPng() is stricter. Below the Canvas threshold the PNG is rasterized through an image and limited to 8,192 px per side and about 16.7 million pixels, which is about 255 rows at the default density and scale: 1. A 1,500-task chart in SVG mode therefore rejects, while a 2,001-task chart (Canvas mode) can succeed. Use exportSvg() or lower scale.
// Trigger a download of the PNG in the browser:
const blob = await gantt.exportPng();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'plan.png';
a.click();
URL.revokeObjectURL(url);

Import JSON / CSV

importJson / importCsv are standalone functions (not instance methods). They validate the input and return plain TaskInput[] / DependencyInput[] you pass to createGantt (or apply via addTask / linkTasks):

import { createGantt } from '@fluxgantt/core';
import { importJson } from '@fluxgantt/core/io';
 
const { tasks, dependencies } = importJson(jsonStringOrObject);
const gantt = createGantt({ tasks, dependencies });
import { importCsv } from '@fluxgantt/core/io';
 
const { tasks } = importCsv(csvString);

To replace the dataset of an ALREADY-LIVE instance (atomically, undoable), use the withIo instance methods of the same name instead: gantt.importJson(input) / gantt.importCsv(csv).

Round-trips and validation

importJson accepts either the full { fluxgantt, tasks, dependencies } envelope produced by exportJson, or a bare { tasks, dependencies } object — so an export can be re-imported. It is strict: malformed input throws an IoValidationError atomically (nothing is partially loaded), and enforces size/count limits to guard against a malicious file. Unknown top-level keys (e.g. a future Pro resources block) are tolerated and ignored.

import { createGantt } from '@fluxgantt/core';
import { importJson, IoValidationError } from '@fluxgantt/core/io';
 
try {
  const { tasks, dependencies } = importJson(untrustedInput);
  createGantt({ tasks, dependencies });
} catch (err) {
  if (err instanceof IoValidationError) {
    // reject the file, show a message — do not best-effort load it
  }
}

A security note

Anything you render came from data provided by someone — treat notes, meta, and color as untrusted. FluxGantt's renderer uses safe DOM APIs (never string-interpolated markup) and whitelist-validates color; CSV export escapes cells to prevent spreadsheet formula injection. You get these protections by using the library's own import/export paths rather than hand-rolling them.