Getting started
@data-xplorer/core draws tree- and graph-shaped data as a space you zoom through. This page
goes from install to a working explorer. Concepts explains the ideas
behind it.
Install
Section titled “Install”npm install @data-xplorer/core pixi.js react react-domreact (18 or 19) and pixi.js (8.13.2 or later 8.x) are peer dependencies: your app provides
them. PixiJS is loaded lazily, the first time an explorer mounts, so it stays out of your page’s
first load. The package is ESM only.
Your first explorer
Section titled “Your first explorer”Everything below is one file. It describes a folder tree as a scene source: given a focus (a
folder path, or null for the top), it returns what to draw there. Zoom into a folder to enter it;
zoom out to leave.
first-render.tsximport { useState } from "react";import { createSceneSource, DataExplorer } from "@data-xplorer/core";
// Your data: here, a small folder tree. Folders hold folders and files (with sizes in bytes).const tree: Record<string, { folders: string[]; files: [string, number][] }> = { "/": { folders: ["/src", "/docs", "/assets"], files: [["README.md", 4_200], ["package.json", 900]] }, "/src": { folders: ["/src/views"], files: [["index.ts", 1_500], ["camera.ts", 9_800], ["layout.ts", 12_400]] }, "/src/views": { folders: [], files: [["Outline.tsx", 6_100], ["Explorer.tsx", 21_000]] }, "/docs": { folders: [], files: [["concepts.md", 7_700], ["guide.md", 5_300]] }, "/assets": { folders: [], files: [["logo.svg", 2_100], ["hero.png", 48_000], ["icon.png", 3_000]] },};const name = (path: string) => path.split("/").pop() || "/";const size = (path: string): number => tree[path]!.files.reduce((sum, [, bytes]) => sum + bytes, 0) + tree[path]!.folders.reduce((sum, f) => sum + size(f), 0);
// A scene source: the scene at each focus (a folder path; `null` is the top), cached for you.const source = createSceneSource<string>({ load(focus) { const path = focus ?? "/"; return { id: path, heading: name(path), clusters: tree[path]!.folders.map((folder) => ({ id: folder, label: name(folder), weight: size(folder), // sizes are relative to the scene's other folders href: `#${folder}`, // every item is also a link in the accessible outline drillsInto: folder, // zooming into it opens this focus shape: "cloud" as const, })), nodes: tree[path]!.files.map(([file, bytes]) => ({ id: file, label: file, sublabel: `${bytes.toLocaleString()} bytes`, weight: bytes, href: `#${path}/${file}`, drillsInto: null, // a file has nothing inside shape: "disc" as const, })), edges: [], }; }, // Zooming out of a folder lands on its cloud in the parent folder. parentOf(path) { const parent = path.slice(0, path.lastIndexOf("/")) || "/"; return { kind: "anchored", focus: parent === "/" ? null : parent, anchorId: path }; },});
export default function FirstRender() { const [focus, setFocus] = useState<string | null>(null); return <DataExplorer source={source} focus={focus} onSettle={setFocus} />;}Three things in it are worth knowing now:
- Clusters and nodes. Folders are
clusters, drawn as soft clouds you can enter; files arenodes, drawn as discs.weightsets each one’s size against its neighbours. drillsIntoandparentOf. An item’sdrillsIntois the focus it opens.parentOfsays where zooming out of a focus leads: here, back to the folder’s own cloud in its parent.focusandonSettle. The explorer moves the camera itself as the viewer zooms.onSettle(focus)tells you where they came to rest, once per burst of motion. Store it (in state, or in your URL) and pass it back asfocus; passing a differentfocus(Back, a deep link) makes the explorer jump there.
The explorer needs no stylesheet. Its canvas fills its container’s width; if nothing gives it a
height, it takes 480 px. Give its container, or .data-explorer__surface, a height to change that.
- Concepts: focus, scenes, packed, radial and fixed layouts, drilling and zooming out, loading, and the accessible outline.
- Events: selection, hover, activation, and getting your own data back.
- Styling and theming: colours, ramps and sizing.