supermemory/packages/memory-graph
nexxeln dfb0c05ab3 add spaces selector with search (#600)
relevant files to review:
\- memory-graph.tsx
\- spaces-dropdown.tsx
\- spaces-dropdown.css.ts
2025-12-02 18:37:24 +00:00
..
src add spaces selector with search (#600) 2025-12-02 18:37:24 +00:00
.gitignore package the graph (#563) 2025-11-19 18:57:56 +00:00
.npmignore package the graph (#563) 2025-11-19 18:57:56 +00:00
package.json add spaces selector with search (#600) 2025-12-02 18:37:24 +00:00
README.md add spaces selector with search (#600) 2025-12-02 18:37:24 +00:00
tsconfig.json add spaces selector with search (#600) 2025-12-02 18:37:24 +00:00
vite.config.ts add spaces selector with search (#600) 2025-12-02 18:37:24 +00:00

@supermemory/memory-graph

Interactive graph visualization component for Supermemory - visualize and explore your memory connections

npm version License: MIT

Features

  • WebGL-powered rendering - Smooth performance with hundreds of nodes
  • Interactive exploration - Pan, zoom, drag nodes, and explore connections
  • Semantic connections - Visualizes relationships based on content similarity
  • Space filtering with search - Dynamically search and filter by spaces/tags
  • Memory limit control - Limit memories per document (50-3000) for performance
  • Controlled/uncontrolled modes - Flexible state management for integration
  • Responsive design - Works seamlessly on mobile and desktop
  • Zero configuration - Works out of the box with automatic CSS injection
  • Lightweight - Tree-shakeable and optimized bundle
  • TypeScript - Full TypeScript support with exported types

Installation

npm install @supermemory/memory-graph
# or
yarn add @supermemory/memory-graph
# or
pnpm add @supermemory/memory-graph
# or
bun add @supermemory/memory-graph

Quick Start

The component accepts document data directly - you fetch the data from your backend, which proxies requests to the Supermemory API with proper authentication.

import { MemoryGraph } from '@supermemory/memory-graph'
import type { DocumentWithMemories } from '@supermemory/memory-graph'

function App() {
  const [documents, setDocuments] = useState<DocumentWithMemories[]>([])
  const [isLoading, setIsLoading] = useState(true)
  const [error, setError] = useState<Error | null>(null)

  useEffect(() => {
    // Fetch from YOUR backend (which proxies to Supermemory API)
    fetch('/api/supermemory-graph')
      .then(res => res.json())
      .then(data => {
        setDocuments(data.documents)
        setIsLoading(false)
      })
      .catch(err => {
        setError(err)
        setIsLoading(false)
      })
  }, [])

  return (
    <MemoryGraph
      documents={documents}
      isLoading={isLoading}
      error={error}
    />
  )
}

Backend Proxy Example

Create an API route in your backend that authenticates and proxies requests to Supermemory:

Next.js API Route

// app/api/supermemory-graph/route.ts
import { NextResponse } from 'next/server'

export async function GET(request: Request) {
  // Add your own auth check here
  const user = await getAuthenticatedUser(request)
  if (!user) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  const response = await fetch('https://api.supermemory.ai/v3/documents/documents', {
    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)
}

API Reference

Props

Prop Type Default Description
documents DocumentWithMemories[] required Array of documents to display
isLoading boolean false Whether data is currently loading
error Error | null null Error that occurred during fetching
variant "console" | "consumer" "console" Visual variant
showSpacesSelector boolean Based on variant Show/hide the spaces filter
children ReactNode - Content to show when no documents
highlightDocumentIds string[] [] Document IDs to highlight
highlightsVisible boolean true Whether highlights are visible

Space & Memory Control Props (Optional)

Prop Type Default Description
selectedSpace string "all" Currently selected space (controlled)
onSpaceChange (spaceId: string) => void - Callback when space changes
onSearchSpaces (query: string) => Promise<string[]> - Async space search function
memoryLimit number 500 Max memories per document when space selected
onMemoryLimitChange (limit: number) => void - Callback when limit changes
isExperimental boolean false Enable experimental features

Pagination Props (Optional)

For large datasets, you can implement pagination:

Prop Type Default Description
isLoadingMore boolean false Whether loading more data
hasMore boolean false Whether more data is available
totalLoaded number documents.length Total documents loaded
loadMoreDocuments () => Promise<void> - Callback to load more

Types

import type {
  DocumentWithMemories,
  MemoryEntry,
  DocumentsResponse,
  MemoryGraphProps,
  MemoryLimit,
  MemoryCountSelectorProps,
  GraphNode,
  GraphEdge,
  MemoryRelation
} from '@supermemory/memory-graph'

// Memory limit can be one of these values
type MemoryLimit = 50 | 100 | 250 | 500 | 1000 | 2000 | 3000

Advanced Usage

With Pagination

import { MemoryGraph } from '@supermemory/memory-graph'

function GraphWithPagination() {
  const [documents, setDocuments] = useState([])
  const [page, setPage] = useState(1)
  const [hasMore, setHasMore] = useState(true)
  const [isLoadingMore, setIsLoadingMore] = useState(false)

  const loadMore = async () => {
    setIsLoadingMore(true)
    const res = await fetch(`/api/supermemory-graph?page=${page + 1}`)
    const data = await res.json()
    setDocuments(prev => [...prev, ...data.documents])
    setHasMore(data.pagination.currentPage < data.pagination.totalPages)
    setPage(p => p + 1)
    setIsLoadingMore(false)
  }

  return (
    <MemoryGraph
      documents={documents}
      isLoading={isLoading}
      isLoadingMore={isLoadingMore}
      hasMore={hasMore}
      loadMoreDocuments={loadMore}
      totalLoaded={documents.length}
    />
  )
}

Custom Empty State

<MemoryGraph documents={[]} isLoading={false}>
  <div className="empty-state">
    <h2>No memories yet</h2>
    <p>Start adding content to see your knowledge graph</p>
  </div>
</MemoryGraph>

Controlled Space Selection & Memory Limiting

Control the selected space and memory limit externally for integration with your app's state management:

import { MemoryGraph } from '@supermemory/memory-graph'

function ControlledGraph() {
  const [selectedSpace, setSelectedSpace] = useState("all")
  const [memoryLimit, setMemoryLimit] = useState(500)
  const [searchResults, setSearchResults] = useState([])

  // Handle space search via your API
  const handleSpaceSearch = async (query: string) => {
    const response = await fetch(`/api/spaces/search?q=${query}`)
    const spaces = await response.json()
    setSearchResults(spaces)
    return spaces
  }

  return (
    <div>
      {/* Display current state */}
      <div className="controls">
        <p>Selected Space: {selectedSpace}</p>
        <p>Memory Limit: {memoryLimit}</p>
        <button onClick={() => {
          setSelectedSpace("all")
          setMemoryLimit(500)
        }}>
          Reset Filters
        </button>
      </div>

      {/* Controlled graph */}
      <MemoryGraph
        documents={documents}
        selectedSpace={selectedSpace}
        onSpaceChange={setSelectedSpace}
        onSearchSpaces={handleSpaceSearch}
        memoryLimit={memoryLimit}
        onMemoryLimitChange={setMemoryLimit}
        variant="console"
        showSpacesSelector={true}
      />
    </div>
  )
}

Uncontrolled Mode (Automatic)

If you don't provide selectedSpace or memoryLimit props, the component manages its own state:

<MemoryGraph
  documents={documents}
  // Component manages space selection and memory limit internally
  onSearchSpaces={handleSpaceSearch} // Still can provide search function
  showSpacesSelector={true}
/>

Space Search Integration

Implement server-side space search for dynamic filtering:

// Frontend
const handleSpaceSearch = async (query: string): Promise<string[]> => {
  const response = await fetch('/api/spaces/search', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ query })
  })
  return response.json()
}

<MemoryGraph
  documents={documents}
  onSearchSpaces={handleSpaceSearch}
  showSpacesSelector={true}
/>

// Backend (Next.js example)
// app/api/spaces/search/route.ts
export async function POST(request: Request) {
  const { query } = await request.json()

  const response = await fetch('https://api.supermemory.ai/v3/search/spaces', {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
    },
    params: { q: query }
  })

  return response.json()
}

Variants

The variant prop controls the visual layout and initial viewport settings:

Variant Initial Zoom Spaces Selector Legend Position Use Case
console 0.8 Shown Bottom-right Full-page dashboard views
consumer 0.5 Hidden Top-right Embedded/widget views
// Full dashboard view
<MemoryGraph
  documents={documents}
  variant="console"
/>

// Embedded widget
<MemoryGraph
  documents={documents}
  variant="consumer"
/>

Interactive Controls

  • Pan: Click and drag the background
  • Zoom: Mouse wheel or pinch on mobile
  • Select Node: Click on any document or memory
  • Drag Nodes: Click and drag individual nodes
  • Fit to View: Auto-fit button to center all content
  • Space Filter: Click the space selector to filter by space
  • Space Search: Type in the search box and press Enter to find spaces
  • Memory Limit: Select a limit (50-3K) when filtering by space

Browser Support

  • Chrome/Edge (latest)
  • Firefox (latest)
  • Safari (latest)
  • Mobile browsers with WebGL support

Requirements

  • React 18+
  • Modern browser with WebGL support

Development

# Install dependencies
bun install

# Build the package
bun run build

# Watch mode for development
bun run dev

# Type checking
bun run check-types

License

MIT

Support