GlucoseIQ

React Adapter

Memoized analytics hooks and SVG components from @glucoseiq/react.

@glucoseiq/react wraps the core analytics and SVG renderers. Use hooks for typed data or components for a rendered SVG.

npm install @glucoseiq/react @glucoseiq/core

React 18 or newer is the only peer dependency. The root is a Client Component package; use @glucoseiq/core directly for server-only work.

Choose the smallest API that fits

APIReturnsBest for
useGlucoseAnalysisAnalyzeGlucoseResultDashboard summaries and retrospective reports
useAGPProfilePercentile bins across a 24-hour clockCustom AGP charts in D3, Visx, Canvas, or another renderer
useGlucoseIQScoreScore, rating, and zoneA compact wellness summary
useMealResponseBaseline, peak, delta, timing, and iAUCPost-meal detail views
useGlucoseLiveLatest reading, derived trend, and stalenessCurrent glucose views
AgpChart, TirBar, TrendTileInline SVG inside a wrapper elementChart-library-free SVG visuals

All hooks accept the same GlucoseReading[] contract as @glucoseiq/core.

Build a report-driven component

'use client'

import type { AnalyzeGlucoseOptions, GlucoseReading } from '@glucoseiq/core'
import { useGlucoseAnalysis } from '@glucoseiq/react'

const options: AnalyzeGlucoseOptions = {
  timeZone: 'America/Detroit',
}

export function Summary({ readings }: { readings: GlucoseReading[] }) {
  const report = useGlucoseAnalysis(readings, options)

  if (!report.valid || !report.timeInRange) {
    return <p>No valid glucose readings are available.</p>
  }

  return (
    <dl>
      <div>
        <dt>Time in range</dt>
        <dd>{report.timeInRange.inRange.percentage.toFixed(1)}%</dd>
      </div>
      <div>
        <dt>GMI</dt>
        <dd>{report.gmi.toFixed(1)}%</dd>
      </div>
      <div>
        <dt>Variability</dt>
        <dd>{report.cv.toFixed(1)}%</dd>
      </div>
    </dl>
  )
}

The hooks memoize their corresponding core computation. Keep the readings array and options object stable when their contents have not changed; recreating either reference on every render causes the analysis to run again.

Add a live view model

useGlucoseLive combines latestReading, computeGlucoseTrend, and minutesSinceLastReading:

import type { GlucoseReading } from '@glucoseiq/core'
import { useGlucoseLive } from '@glucoseiq/react'

const liveOptions = {
  windowMin: 15,
  refreshMs: 30_000,
} as const

function CurrentGlucose({ readings }: { readings: GlucoseReading[] }) {
  const live = useGlucoseLive(readings, liveOptions)

  if (!live.latest || live.minutesSince === null) {
    return <p>No current reading.</p>
  }

  if (live.minutesSince < 0) {
    return (
      <p role="status">
        The latest reading time is ahead of the current clock.
      </p>
    )
  }

  return (
    <section aria-live="polite">
      <strong>{live.latest.value} {live.latest.unit}</strong>
      <span>{live.trend.trend}</span>
      <time dateTime={live.latest.timestamp}>
        {Math.round(live.minutesSince)} min ago
      </time>
    </section>
  )
}

Omit refreshMs to disable interval refresh. When provided, it must be a whole number of milliseconds from 1 through 2_147_483_647; invalid values throw DomainError with code INVALID_OPTION before a timer is scheduled. A valid value only advances the staleness clock. It does not poll a CGM source or add readings. Your application remains responsible for transport and for updating the array when data arrives.

See Build a Live Glucose View for a polling example and failure states.

Use the SVG components

Each SVG includes a concise image label. Pair it with an adjacent text summary that states the values and trend a reader needs to understand. A visible figcaption also serves people who cannot distinguish a chart's colors or shapes.

import type { GlucoseReading, TIRBarOptions } from '@glucoseiq/core'
import { AgpChart, TirBar, TrendTile } from '@glucoseiq/react'

const tirOptions: TIRBarOptions = { theme: 'dark' }
const agpOptions = {
  theme: 'dark',
  timeZone: 'America/Detroit',
  title: 'Last 14 days',
} as const

interface ChartSummaries {
  current: string
  timeInRange: string
  agp: string
}

interface ChartsProps {
  readings: GlucoseReading[]
  summaries: ChartSummaries
}

export function Charts({ readings, summaries }: ChartsProps) {
  return (
    <>
      <figure>
        <TrendTile readings={readings} />
        <figcaption>{summaries.current}</figcaption>
      </figure>
      <figure>
        <TirBar readings={readings} options={tirOptions} />
        <figcaption>{summaries.timeInRange}</figcaption>
      </figure>
      <figure>
        <AgpChart
          readings={readings}
          options={agpOptions}
          className="dashboard-chart"
        />
        <figcaption>{summaries.agp}</figcaption>
      </figure>
    </>
  )
}

Build the summary strings from the same report or live model as the SVG. Include the numeric values and labels that color, position, or a trend glyph would otherwise carry alone.

Each component memoizes a zero-dependency renderer from @glucoseiq/core and inlines the returned SVG inside a div. className and style apply to that wrapper. Dimensions, theme, time zone, and titles belong in the renderer's options object.

Use data APIs for custom rendering

The SVG components provide fixed renderers. Use the hooks or core APIs for custom axes, interaction, Canvas, native charts, or a different accessibility model.

Where React fits in the architecture

Keep normalization, credentials, and long-running data access outside the UI component. A typical application:

  1. Fetches and normalizes source payloads on the server or data layer.
  2. Passes GlucoseReading[] into a client-facing component.
  3. Uses hooks to derive immutable view data.
  4. Renders that data with its own design system or the optional SVG components.

Follow Build a Glucose Dashboard for a working example.

On this page