FlameGraph

July 9, 2026 ยท View on GitHub

A React component that renders interactive flame graphs for profiling data visualization using WebGL.

Purpose

The FlameGraph component renders performance profiling data from Go's pprof format as an interactive flame graph visualization. It uses WebGL for high-performance rendering and supports zooming, panning, frame selection, and hover interactions with tooltips.

Props

PropTypeDefaultDescription
profileProfilerequiredThe parsed pprof profile data to visualize
widthnumber | string'100%'Width of the flame graph container
heightnumber | string-Height of the flame graph (auto-calculated if not provided)
primaryColorstring'#ff4444'Primary color for flame graph frames
secondaryColorstring'#ffcc66'Secondary color for flame graph frames
backgroundColorstring'#1e1e1e'Background color of the container
textColorstring'#ffffff'Text color for frame labels
fontFamilystringSystem font stackFont family for text rendering
shadowOpacitynumber0.3Opacity of frame shadows
selectedOpacitynumber1.0Opacity of selected frames
hoverOpacitynumber0.9Opacity of hovered frames
unselectedOpacitynumber0.75Opacity of unselected frames
framePaddingnumber5Padding around frames in pixels
zoomOnScrollbooleanfalseEnable zoom functionality with scroll wheel
scrollZoomSpeednumber0.05Speed of scroll zoom
scrollZoomInvertedbooleanfalseInvert scroll zoom direction
selectedFrameIdstring | null-ID of the currently selected frame
highlightedFrameIdsstring[] | null-IDs of frames to highlight (e.g. search matches); matches render at full opacity, other frames are dimmed. null clears the highlight
onFrameClickfunction-Callback when a frame is clicked
onZoomChangefunction-Callback when zoom level changes
onAnimationCompletefunction-Callback when animations complete

Usage Examples

Basic Usage with Profile Loading

import React, { useState, useEffect } from 'react'
import { FlameGraph } from './FlameGraph'
import { fetchProfile, Profile } from '../parser'

function MyComponent() {
  const [profile, setProfile] = useState<Profile | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    fetchProfile('/path/to/profile.pprof')
      .then(setProfile)
      .catch(err => setError(err.message))
      .finally(() => setLoading(false))
  }, [])

  if (loading) return <div>Loading profile...</div>
  if (error) return <div>Error: {error}</div>
  if (!profile) return <div>No profile data</div>

  return (
    <FlameGraph
      profile={profile}
      width="100%"
      height={500}
    />
  )
}

Loading from API Endpoint

import React, { useState, useEffect } from 'react'
import { FlameGraph } from './FlameGraph'
import { fetchProfile, Profile } from '../parser'

function ProfileViewer({ profileUrl }: { profileUrl: string }) {
  const [profile, setProfile] = useState<Profile | null>(null)

  useEffect(() => {
    const loadProfile = async () => {
      try {
        const profileData = await fetchProfile(profileUrl)
        setProfile(profileData)
      } catch (error) {
        console.error('Failed to load profile:', error)
      }
    }

    loadProfile()
  }, [profileUrl])

  return profile ? (
    <FlameGraph
      profile={profile}
      width="100%"
      height={500}
    />
  ) : (
    <div>Loading...</div>
  )
}

With Custom Colors and Interactions

import { FlameGraph } from './FlameGraph'
import { Profile, FrameData, FlameNode } from '../types'

function InteractiveFlameGraph({ profileData }: { profileData: Profile }) {
  const handleFrameClick = (
    frame: FrameData | null,
    stackTrace: FlameNode[],
    children: FlameNode[]
  ) => {
    if (frame) {
      console.log(`Clicked frame: ${frame.name}`)
      console.log(`Stack depth: ${stackTrace.length}`)
      console.log(`Children count: ${children.length}`)
    }
  }

  return (
    <FlameGraph
      profile={profileData}
      width="100%"
      height={600}
      primaryColor="#3b82f6"
      secondaryColor="#ef4444"
      backgroundColor="#0f172a"
      textColor="#f1f5f9"
      zoomOnScroll={true}
      onFrameClick={handleFrameClick}
    />
  )
}

With External Frame Selection

import { useState } from 'react'
import { FlameGraph } from './FlameGraph'

function ControlledFlameGraph({ profileData }) {
  const [selectedFrameId, setSelectedFrameId] = useState<string | null>(null)

  return (
    <div>
      <button onClick={() => setSelectedFrameId('root/main/server')}>
        Select Server Frame
      </button>
      <FlameGraph
        profile={profileData}
        selectedFrameId={selectedFrameId}
        onFrameClick={(frame) => {
          setSelectedFrameId(frame?.id || null)
        }}
      />
    </div>
  )
}

Key Features

  • WebGL Rendering: High-performance visualization using WebGL
  • Interactive Navigation: Click to zoom into frames, click empty space to zoom out
  • Pan & Zoom: Mouse drag to pan when zoomed in, optional scroll wheel zoom
  • Hover Tooltips: Shows frame details on hover via FlameGraphTooltip
  • Responsive: Automatically adjusts to container size changes
  • Auto-height Mode: Calculates optimal height based on stack depth
  • Error Handling: Graceful fallback when WebGL is unavailable
  • Keyboard Accessible: Supports external frame selection via props

Technical Details

The component uses a FlameGraphRenderer class for WebGL-based rendering and includes:

  • Canvas with device pixel ratio support for crisp rendering
  • ResizeObserver for responsive behavior
  • Mouse interaction handling for clicks, drags, and hovers
  • Animation support with completion callbacks
  • Automatic cleanup of WebGL resources
  • FlameGraphTooltip: Displays frame information on hover
  • FullFlameGraph: Complete flame graph with controls and details panels
  • HottestFramesBar: Complementary navigation component