Skip to content

Memory Graph

Interactive visualization for documents, memories and connections

Memory Graph is a React component that visualizes your Supermemory documents and memories as an interactive network. Documents appear as rectangular nodes, memories as hexagonal nodes, and connections between them show relationships and similarity.

@supermemory/memory-graph on npm

Installation

bash
npm install @supermemory/memory-graph

Requirements: React 18.0.0 or higher

Quick Start

tsx
'use client'; // For Next.js App Router

import { MemoryGraph } from '@supermemory/memory-graph';
import type { DocumentWithMemories } from '@supermemory/memory-graph';
import { useEffect, useState } from 'react';

export default function GraphPage() {
  const [documents, setDocuments] = useState<DocumentWithMemories[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch('/api/graph')
      .then(res => res.json())
      .then(data => {
        setDocuments(data.documents);
        setIsLoading(false);
      })
      .catch(err => {
        setError(err);
        setIsLoading(false);
      });
  }, []);

  return (
    <div style={{ height: '100vh' }}>
      
    </div>
  );
}

Backend API Route

Create an API route to fetch documents from Supermemory:

typescript
// app/api/graph/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
  const response = await fetch(' {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
    },
    body: JSON.stringify({
      page: 1,
      limit: 500,
      sort: 'createdAt',
      order: 'desc',
    }),
  });

  const data = await response.json();
  return NextResponse.json(data);
}
typescript
// pages/api/graph.ts
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const response = await fetch(' {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
    },
    body: JSON.stringify({ page: 1, limit: 500, sort: 'createdAt', order: 'desc' }),
  });

  const data = await response.json();
  res.json(data);
}
javascript
app.get('/api/graph', async (req, res) => {
  const response = await fetch(' {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
    },
    body: JSON.stringify({ page: 1, limit: 500, sort: 'createdAt', order: 'desc' }),
  });

  const data = await response.json();
  res.json(data);
});

WARNING

Never expose your Supermemory API key to the client. Always fetch data through your backend.


Variants

Console Variant - Full-featured dashboard view (0.8x zoom, space selector visible):

tsx

Consumer Variant - Embedded widget view (0.5x zoom, space selector hidden):

tsx

Examples

With Pagination

tsx
'use client';

import { MemoryGraph } from '@supermemory/memory-graph';
import { useCallback, useEffect, useState } from 'react';

export default function PaginatedGraph() {
  const [documents, setDocuments] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [isLoading, setIsLoading] = useState(true);
  const [isLoadingMore, setIsLoadingMore] = useState(false);

  useEffect(() => { fetchPage(1, false); }, []);

  const fetchPage = async (pageNum, append) => {
    pageNum === 1 ? setIsLoading(true) : setIsLoadingMore(true);

    const res = await fetch(`/api/graph?page=${pageNum}&limit=100`);
    const data = await res.json();

    append ? setDocuments(prev => [...prev, ...data.documents]) : setDocuments(data.documents);
    setHasMore(data.pagination.currentPage < data.pagination.totalPages);
    setIsLoading(false);
    setIsLoadingMore(false);
  };

  const loadMore = useCallback(async () => {
    if (!isLoadingMore && hasMore) {
      const nextPage = page + 1;
      setPage(nextPage);
      await fetchPage(nextPage, true);
    }
  }, [page, hasMore, isLoadingMore]);

  return (
    
  );
}

Highlighting Search Results

tsx
 0}
/>

Controlled Space Selection

tsx

Custom Empty State

tsx

  <div style={{ textAlign: 'center', padding: '2rem' }}>
    <h2>No memories yet</h2>
    <p>Add content to see your knowledge graph</p>
  </div>

Props Reference

Core Props

PropTypeDefaultDescription
documentsDocumentWithMemories[]requiredArray of documents to display
isLoadingbooleanfalseShows loading indicator
errorError | nullnullError to display
variant"console" | "consumer""console"Visual variant
childrenReactNode-Custom empty state content

Pagination Props

PropTypeDefaultDescription
isLoadingMorebooleanfalseShows indicator when loading more
hasMorebooleanfalseWhether more documents available
totalLoadednumber-Total documents currently loaded
loadMoreDocuments() => Promise<void>-Callback to load more
autoLoadOnViewportbooleantrueAuto-load when 80% visible

Display Props

PropTypeDefaultDescription
showSpacesSelectorbooleanvariant-basedShow space filter dropdown
highlightDocumentIdsstring[][]Document IDs to highlight
highlightsVisiblebooleantrueWhether highlights shown
occludedRightPxnumber0Pixels occluded on right

Controlled State Props

PropTypeDescription
selectedSpacestringCurrently selected space (use "all" for all)
onSpaceChange(spaceId: string) => voidCallback when space changes
memoryLimitnumberMax memories per document when space selected

Data Types

DocumentWithMemories

typescript
interface DocumentWithMemories {
  id: string;
  customId?: string | null;
  title?: string | null;
  content?: string | null;
  summary?: string | null;
  url?: string | null;
  source?: string | null;
  type?: string | null;
  status: 'pending' | 'processing' | 'done' | 'failed';
  metadata?: Record<string, string | number | boolean> | null;
  createdAt: string | Date;
  updatedAt: string | Date;
  memoryEntries: MemoryEntry[];
}

MemoryEntry

typescript
interface MemoryEntry {
  id: string;
  documentId: string;
  content: string | null;
  summary?: string | null;
  title?: string | null;
  type?: string | null;
  metadata?: Record<string, string | number | boolean> | null;
  createdAt: string | Date;
  updatedAt: string | Date;
  spaceContainerTag?: string | null;
  relation?: 'updates' | 'extends' | 'derives' | null;
  isLatest?: boolean;
  spaceId?: string | null;
}

Exports

Components

typescript
import {
  MemoryGraph,
  GraphCanvas,
  Legend,
  LoadingIndicator,
  NodeDetailPanel,
  SpacesDropdown
} from '@supermemory/memory-graph';

Hooks

typescript
import { useGraphData, useGraphInteractions } from '@supermemory/memory-graph';

Constants

typescript
import { colors, GRAPH_SETTINGS, LAYOUT_CONSTANTS } from '@supermemory/memory-graph';

Performance

The graph handles hundreds of nodes efficiently through:

  • Canvas-based rendering (not DOM elements)
  • Viewport culling (only draws visible nodes)
  • Level-of-detail optimization (simplifies when zoomed out)
  • Change-based rendering (only redraws when state changes)

For very large datasets (1000+ documents), use pagination to load data in chunks.

Browser Support

Works in all modern browsers supporting Canvas 2D API, ES2020, and CSS custom properties. Tested on Chrome, Firefox, Safari, and Edge.

Built as an internal reference companion.