npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

funhi-chart-library

v1.0.1

Published

Professional multi-timeframe trading chart library with real-time updates and drawing tools

Readme

@funhi/chart-library

Professional multi-timeframe trading chart library for React applications. Built with TypeScript and powered by Lightweight Charts.

Version License React

Features

Multi-Timeframe Support

  • 1m, 5m, 15m, 1h, 4h, 1d timeframes
  • Automatic aggregation from 1-minute candles
  • Smooth timeframe switching

📊 Professional Charting

  • Candlestick charts with volume
  • Real-time price updates
  • Gap filling for continuous display
  • Customizable appearance

🎨 Customizable

  • Light and dark themes
  • Custom watermarks
  • Configurable timeframes
  • Flexible styling

Performance Optimized

  • Efficient candle aggregation
  • Smart data loading
  • Minimal re-renders
  • Hardware-accelerated rendering

🔌 Easy Integration

  • Simple API
  • TypeScript support
  • Flexible data providers
  • No vendor lock-in

Installation

npm install @funhi/chart-library

or

yarn add @funhi/chart-library

or

pnpm add @funhi/chart-library

Quick Start

import { FunhiTradingChart } from '@funhi/chart-library';
import type { CandleData } from '@funhi/chart-library';

function App() {
  const [marketCap, setMarketCap] = useState(50000);
  
  // Provide candle data
  const handleLoadCandles = async (mint: string, timeFrame: number, limit: number): Promise<CandleData[]> => {
    const response = await fetch(`/api/candles/${mint}?timeFrame=${timeFrame}&limit=${limit}`);
    const data = await response.json();
    return data.candles;
  };
  
  // Save candles (optional)
  const handleSaveCandles = (mint: string, candles: CandleData[], timeFrame: number) => {
    fetch(`/api/candles/${mint}`, {
      method: 'POST',
      body: JSON.stringify({ candles, timeFrame })
    });
  };
  
  return (
    <FunhiTradingChart
      mint="7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
      ticker="FUNHI"
      tokenName="Funhi Token"
      realtimeMarketCap={marketCap}
      onLoadCandles={handleLoadCandles}
      onSaveCandles={handleSaveCandles}
      height={500}
    />
  );
}

API Reference

Props

Required Props

| Prop | Type | Description | |------|------|-------------| | mint | string | Token mint address (identifier) |

Optional Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | ticker | string | - | Token ticker symbol (e.g., "FUNHI") | | tokenName | string | - | Full token name | | height | number | 384 | Chart height in pixels | | width | string | '100%' | Chart width (CSS value) | | realtimeMarketCap | number | - | Real-time market cap for live updates | | isConnected | boolean | true | Connection status indicator | | theme | 'light' \| 'dark' | 'light' | Chart theme | | showWatermark | boolean | true | Show token pair watermark | | showTimeframes | boolean | true | Show timeframe selector | | showStatus | boolean | true | Show connection status | | className | string | '' | Additional CSS classes for chart | | containerClassName | string | '' | Additional CSS classes for container |

Callback Props

| Prop | Type | Description | |------|------|-------------| | onLoadCandles | (mint: string, timeFrame: number, limit: number) => Promise<CandleData[]> | Load historical candles | | onSaveCandles | (mint: string, candles: CandleData[], timeFrame: number) => void | Save candles to storage | | onMarketCapUpdate | (marketCap: number) => void | Called when market cap updates |

Advanced Props

| Prop | Type | Description | |------|------|-------------| | availableTimeframes | TimeFrame[] | Custom timeframe configuration |

Types

interface CandleData {
  time: number;        // Unix timestamp
  open: number;        // Opening price
  high: number;        // Highest price
  low: number;         // Lowest price
  close: number;       // Closing price
  volume: number;      // Trading volume
}

interface TimeFrame {
  label: string;       // Display label (e.g., "5m")
  value: string;       // Value identifier
  seconds: number;     // Duration in seconds
}

Examples

Basic Usage

import { FunhiTradingChart } from '@funhi/chart-library';

<FunhiTradingChart
  mint="7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
  ticker="FUNHI"
/>

With Real-Time Updates

import { FunhiTradingChart } from '@funhi/chart-library';
import { useWebSocket } from './hooks/useWebSocket';

function TokenChart({ mint, ticker }: { mint: string; ticker: string }) {
  const { marketCap, isConnected } = useWebSocket(mint);
  
  return (
    <FunhiTradingChart
      mint={mint}
      ticker={ticker}
      realtimeMarketCap={marketCap}
      isConnected={isConnected}
    />
  );
}

With Custom Data Provider

import { FunhiTradingChart } from '@funhi/chart-library';
import axios from 'axios';

function CustomChart() {
  const loadCandles = async (mint: string, timeFrame: number, limit: number) => {
    const { data } = await axios.get(`https://api.example.com/ohlcv/${mint}`, {
      params: { timeFrame, limit }
    });
    return data.candles;
  };
  
  const saveCandles = (mint: string, candles: CandleData[], timeFrame: number) => {
    axios.post(`https://api.example.com/ohlcv/${mint}`, {
      candles,
      timeFrame
    });
  };
  
  return (
    <FunhiTradingChart
      mint="ABC123..."
      ticker="TOKEN"
      onLoadCandles={loadCandles}
      onSaveCandles={saveCandles}
    />
  );
}

Dark Theme

<FunhiTradingChart
  mint="ABC123..."
  ticker="DARK"
  theme="dark"
  height={600}
/>

Custom Timeframes

const customTimeframes = [
  { label: '1m', value: '1m', seconds: 60 },
  { label: '5m', value: '5m', seconds: 300 },
  { label: '1h', value: '1h', seconds: 3600 }
];

<FunhiTradingChart
  mint="ABC123..."
  ticker="CUSTOM"
  availableTimeframes={customTimeframes}
/>

Minimal Configuration

// Just the chart, no controls
<FunhiTradingChart
  mint="ABC123..."
  showTimeframes={false}
  showStatus={false}
  showWatermark={false}
  height={300}
/>

Data Flow

1-Minute Candles as Source of Truth

The library uses 1-minute candles as the single source of truth:

Real-time Price Update
  ↓
Create/Update 1-Minute Candle
  ↓
Store in oneMinuteCandles array
  ↓
Auto-aggregate to current timeframe (5m, 1h, etc.)
  ↓
Display aggregated candles

Timeframe Switching

User clicks "1h" button
  ↓
Calculate required 1m candles (200 hours × 60 minutes = 12,000)
  ↓
Load 1m candles via onLoadCandles callback
  ↓
Aggregate to 1-hour candles
  ↓
Display 200 1-hour candles

Backend Integration

Required Backend Endpoint

Your backend should provide an endpoint to fetch candles:

// GET /api/ohlcv/candles/:mint?timeFrame=60&limit=200
{
  "success": true,
  "data": {
    "mint": "ABC123...",
    "timeFrame": 60,
    "candles": [
      {
        "time": 1697740800,
        "open": 50000,
        "high": 51000,
        "low": 49000,
        "close": 50500,
        "volume": 1000
      }
      // ... more candles
    ],
    "count": 200
  }
}

Database Schema

Store only 1-minute candles in your database:

CREATE TABLE ohlcv (
  id SERIAL PRIMARY KEY,
  mint VARCHAR(44) NOT NULL,
  timestamp INTEGER NOT NULL,
  open DECIMAL(20, 8) NOT NULL,
  high DECIMAL(20, 8) NOT NULL,
  low DECIMAL(20, 8) NOT NULL,
  close DECIMAL(20, 8) NOT NULL,
  volume DECIMAL(20, 8) DEFAULT 0,
  timeFrame INTEGER NOT NULL DEFAULT 60,
  createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  UNIQUE(mint, timestamp, timeFrame)
);

CREATE INDEX idx_ohlcv_mint_timeframe ON ohlcv(mint, timeFrame, timestamp DESC);

The library will aggregate 1m candles client-side for other timeframes.

Real-Time Updates

Using WebSocket

import { FunhiTradingChart } from '@funhi/chart-library';
import { useEffect, useState } from 'react';
import io from 'socket.io-client';

function RealtimeChart({ mint, ticker }: { mint: string; ticker: string }) {
  const [marketCap, setMarketCap] = useState(0);
  const [connected, setConnected] = useState(false);
  
  useEffect(() => {
    const socket = io('wss://your-api.com');
    
    socket.on('connect', () => setConnected(true));
    socket.on('disconnect', () => setConnected(false));
    
    socket.on('chartUpdate', (data: any) => {
      if (data.mint === mint) {
        setMarketCap(data.marketCap);
      }
    });
    
    socket.emit('subscribe', { mint });
    
    return () => {
      socket.emit('unsubscribe', { mint });
      socket.disconnect();
    };
  }, [mint]);
  
  return (
    <FunhiTradingChart
      mint={mint}
      ticker={ticker}
      realtimeMarketCap={marketCap}
      isConnected={connected}
    />
  );
}

Using REST API Polling

import { FunhiTradingChart } from '@funhi/chart-library';
import { useEffect, useState } from 'react';

function PollingChart({ mint, ticker }: { mint: string; ticker: string }) {
  const [marketCap, setMarketCap] = useState(0);
  
  useEffect(() => {
    const fetchPrice = async () => {
      const response = await fetch(`/api/price/${mint}`);
      const data = await response.json();
      setMarketCap(data.marketCap);
    };
    
    // Poll every 10 seconds
    const interval = setInterval(fetchPrice, 10000);
    fetchPrice(); // Initial fetch
    
    return () => clearInterval(interval);
  }, [mint]);
  
  return (
    <FunhiTradingChart
      mint={mint}
      ticker={ticker}
      realtimeMarketCap={marketCap}
    />
  );
}

Styling

Using Tailwind CSS

The chart uses Tailwind CSS classes. Make sure Tailwind is configured in your project:

// Your component
<div className="max-w-7xl mx-auto p-4">
  <FunhiTradingChart
    mint="ABC123..."
    ticker="TOKEN"
    containerClassName="shadow-lg rounded-lg"
  />
</div>

Custom Styling

<FunhiTradingChart
  mint="ABC123..."
  ticker="TOKEN"
  className="border-2 border-blue-500"
  containerClassName="bg-gray-100 p-4 rounded-xl"
  height={600}
  width="90%"
/>

Advanced Usage

Custom Data Aggregation

import { candleAggregation, CandleData } from '@funhi/chart-library';

// Aggregate 1m candles to 5m manually
const oneMinuteCandles: CandleData[] = [...]; // Your 1m data
const fiveMinuteCandles = candleAggregation.aggregateCandles(oneMinuteCandles, 300);

// Fill gaps
const filledCandles = candleAggregation.fillGaps(fiveMinuteCandles, 300);

Validation

import { candleAggregation } from '@funhi/chart-library';

const isValid = candleAggregation.isValidCandle({
  time: 1697740800,
  open: 50000,
  high: 51000,
  low: 49000,
  close: 50500,
  volume: 1000
});

Browser Support

  • Chrome/Edge: Latest 2 versions
  • Firefox: Latest 2 versions
  • Safari: Latest 2 versions
  • Mobile browsers: iOS Safari 14+, Chrome Android

Performance

Metrics

  • Initial Load: < 200ms
  • Timeframe Switch: < 100ms (cached data)
  • Real-time Update: < 16ms (60 FPS)
  • Memory Usage: ~5-25 MB depending on timeframe and data

Optimization Tips

  1. Limit Historical Data: Don't load more than needed

    // Good: Load only what's necessary
    onLoadCandles={(mint, timeFrame, limit) => fetchCandles(mint, timeFrame, Math.min(limit, 500))}
  2. Debounce Real-time Updates: For high-frequency data

    const debouncedMarketCap = useMemo(() => debounce(marketCap, 1000), [marketCap]);
  3. Lazy Load: Use dynamic imports

    const FunhiChart = dynamic(() => import('@funhi/chart-library'), { ssr: false });

TypeScript

The library is written in TypeScript and provides full type definitions:

import type { 
  CandleData, 
  TimeFrame, 
  FunhiTradingChartProps 
} from '@funhi/chart-library';

const MyComponent: React.FC = () => {
  const candles: CandleData[] = [];
  const timeframes: TimeFrame[] = [];
  
  // Full type safety
  return <FunhiTradingChart mint="..." />;
};

Troubleshooting

Chart not displaying

Issue: Chart container is not visible

Solution: Ensure parent container has defined dimensions

<div style={{ width: '100%', height: '500px' }}>
  <FunhiTradingChart mint="..." />
</div>

No data showing

Issue: Chart shows "No market data available"

Solutions:

  1. Verify onLoadCandles callback is provided and returns data
  2. Check that realtimeMarketCap is updating
  3. Inspect browser console for errors

Timeframe switching not working

Issue: All timeframes show same number of candles

Solution: Ensure onLoadCandles returns enough 1-minute candles:

  • 5m timeframe: ~1,000 1m candles needed for 200 5m candles
  • 1h timeframe: ~12,000 1m candles needed for 200 1h candles

Contributing

We welcome contributions! Please see our Contributing Guide.

License

MIT © Funhi Team

Support

  • Documentation: https://github.com/funhi/chart-library
  • Issues: https://github.com/funhi/chart-library/issues
  • Discord: https://discord.gg/funhi

Changelog

v1.0.0 (2025-10-18)

  • Initial release
  • Multi-timeframe support (1m, 5m, 15m, 1h, 4h, 1d)
  • Real-time market cap updates
  • Light and dark themes
  • Watermark support
  • Automatic candle aggregation
  • Gap filling for continuous display
  • TypeScript support

Credits

Built with:


Made with ❤️ by the Funhi Team