031 / GOOGLE CHARTS / LINE

Google Core Line Chart

Google Charts

Google Visualization corechart line with dark theme styling, smooth spline interpolation, and singleton loader deduplication.

SPEC
#031
ENGINE
Google Charts
FAMILY
Line
RENDERER
google-runtime
STATUS
stable

Installation

PLOTCN/REGISTRY/GOOGLE-LINE/SOURCE
pnpm dlx shadcn@latest add @plotcn/google-line

Checking public registry…

View local registry JSON
REGISTRY direct URL·ENGINE Google Charts·FILES 1·DEPENDENCIES 2

Copied as source. No Plotcn runtime required.

× 340Container width
Measuring preview...
GOOGLE · GOOGLE-RUNTIME · 0 × 340pxMotion enabled · ResizeObserver
Component Specifications
BEST FOR
Time-series continuous trends, financial telemetry, and Google ecosystem dashboards
DATA MODEL
Categorical / Temporal X-axis label + Numeric continuous Y value
INTERACTION
Hover datum focus crosshair, automatic dark glass tooltip
RESPONSIVE
Container-driven via ResizeObserver with frame-synced redraw
ANIMATION
Google native easing transition on initial mount and update
RUNTIME
Google Visualization corechart (cdn.gstatic.com, lazy-loaded)
01 / Component Usage

Basic & Interactive Integration

Import GoogleLine directly into your React client component. Provide an array of observations conforming to the data shape.

page.tsx
import { GoogleLine } from "@/components/charts/google/google-line"

const data = [
  {
    "label": "Jan",
    "value": 186
  },
  {
    "label": "Feb",
    "value": 305
  },
  {
    "label": "Mar",
    "value": 237
  },
  {
    "label": "Apr",
    "value": 273
  }
]

export default function ChartDemo() {
  return (
    <div className="w-full max-w-xl h-80">
      <GoogleLine data={data} color="#10b981" />
    </div>
  )
}

Requires a client context ('use client') for DOM lifecycle and container measurement.

Expected ResultLive 220px
Container-aware width · 200px height
02 / Data Contract

Input Schema & Coordinates

Expects an array of objects. By default, records require a label (string/date) and a value (number). Keys are fully customizable via labelKey and valueKey.

Chart data contract
FieldTypeRequiredMeaning
labelstring | number | DateYesDomain coordinate for the horizontal axis. Formatted as category or timestamp.
valuenumberYesMetric magnitude mapped to vertical coordinate. Finite positive or negative numbers.
[key: string]anyNoAdditional metadata keys (e.g. metadata tooltip, category tag) passed safely without interfering.
Null / Missing Value Policy:

Null or undefined values produce a line gap. Missing points do not crash the visualization.

Coordinate Ordering Policy:

Records should be ordered monotonically by X-axis coordinate to ensure contiguous left-to-right drawing.

Example Observation Payload:
[
  {
    "label": "Jan",
    "value": 186
  },
  {
    "label": "Feb",
    "value": 305
  },
  {
    "label": "Mar",
    "value": 237
  },
  {
    "label": "Apr",
    "value": 273
  },
  {
    "label": "May",
    "value": 309
  },
  {
    "label": "Jun",
    "value": 414
  }
]
03 / Component API & Styling

Props Reference & Interactive Prop Explorer

Inspect every component property, customize semantic color roles live with instant visual feedback, and copy production-ready code with active prop configurations.

Colors & Appearance Configuration

Customize primary, reference, or annotation series colors. Defaults derive from Plotcn theme tokens.

Generated Usage Code (Live Props):
<GoogleLine
  data={data}
  xKey="date"
  seriesKey="value"
/>
Interactive Prop Preview Lab
curveType"function" | "none"

Controls curve interpolation geometry. 'function' produces a smooth spline; 'none' draws straight linear segments.

Select value to preview live:
Active: curveType="function"Default: "function"
pointSizenumber

Radius in pixels of individual data point markers along the line. Set to 0 to show line only.

Select value to preview live:
Active: pointSize={0}Default: 0
showGridboolean

Toggles visibility of horizontal background gridlines on the vertical numeric axis.

Select value to preview live:
Active: showGrid={true}Default: true
colorstring

Primary theme stroke color. Accepts CSS variables (e.g. var(--chart-2)) or valid hex/rgb strings.

Select value to preview live:#10b981
Active: color="#10b981"Default: "var(--chart-1, #10b981)"
All Properties (9)
Component properties
PropertyTypeDefaultRequiredDescription
dataReq
GoogleLineDatum[]Yes

Array of structured data records to bind to the Google Visualization DataTable.

Best for: Primary dataset source

"function" | "none""function"No

Controls curve interpolation geometry. 'function' produces a smooth spline; 'none' draws straight linear segments.

Best for: Continuous smooth metric trends (function) or discrete financial step trends (none)

number0No

Radius in pixels of individual data point markers along the line. Set to 0 to show line only.

Best for: Highlighting individual discrete observations (e.g. 5) vs clean minimalist lines (0)

booleantrueNo

Toggles visibility of horizontal background gridlines on the vertical numeric axis.

Best for: Aiding value readability across wide containers

string"var(--chart-1, #10b981)"No

Primary theme stroke color. Accepts CSS variables (e.g. var(--chart-2)) or valid hex/rgb strings.

Best for: Brand color harmonization and multi-chart dashboards

number | string320No

Total height of the chart container in pixels or CSS units.

Best for: Dashboard cards and full-width analytics rows

string"label"No

Key name in data objects corresponding to the horizontal axis category or date.

Best for: Mapping custom API payloads without client-side reshaping

string"value"No

Key name in data objects corresponding to the vertical numeric value.

Best for: Selecting specific metric columns from normalized records

stringundefinedNo

Tailwind CSS classes or custom stylesheet class applied to the outer figure container.

04 / Cookbook & States

Component Variants & Edge States

Production cookbooks showcasing configuration variants alongside verified handling of loading, empty data, and network error states.

Smooth Spline (Default)

Continuous metric visualization using Google's function spline interpolation.

<GoogleLine data={monthlyData} curveType="function" color="#10b981" />

Linear Line with Point Markers

Straight line interpolation with highlighted 5px circular markers at each observation.

<GoogleLine data={monthlyData} curveType="none" pointSize={5} color="#0ea5e9" />

Clean Gridless Sparkline

Minimalist presentation with gridlines suppressed for clean telemetry cards.

<GoogleLine data={monthlyData} showGrid={false} color="#8b5cf6" height={220} />
Lifecycle & Exception States
01. Loading State

Skeletons indicate runtime fetch or pending data queries.

02. Empty Data State

Handles empty collections ([]) gracefully without crashing.

03. Error State

Graceful failure banner when data source or script fails.

05 / Responsive Lab

Container-Driven Breakpoints

Uses a ResizeObserver on the container element. When dimensions change, Google Line recalculates SVG coordinate paths to match parent bounds.

Desktop
1100px+

Full horizontal grid span, 85% width chart area, all categorical X-axis labels rendered.

Tablet
768px

Chart area automatically adjusts; axis labels rotate or decimate if space constraints require.

Mobile
390px

Tightened margins, touch-friendly tap targets, single-column dashboard layout compatibility.

Tablet Preview (768px Container Constraint)
Mobile Preview (390px Container Constraint)
06 / Assistive Technology

Accessibility & Navigation Standards

Encapsulated within semantic HTML5 <figure> with explicit region landmark, assistive screen-reader summary paragraph, and color-contrast verified typography.

Semantic Role & Landmark

Container mounts as figure[role="region"] with aria-label with explicit assistive label.

Color-Independent Legibility

Line geometry is complemented by coordinate axis baseline and optional distinct point markers to avoid reliance on color hue alone.

Screen Reader Summary

Embeds visually hidden summary (.sr-only) declaring: “Line chart rendered with Google Charts corechart package displaying trend values.

Reduced Motion Support

Honors prefers-reduced-motion by suppressing transition duration.

Keyboard Interaction Model
Keyboard interaction model
KeyAction
TabFocus chart container and navigate through interactive regions.
EscapeDismiss active tooltip overlay or popover inspector.
07 / Source Anatomy

Internal Architecture & File Dependencies

Source-first ownership model. Inspect the exact component call tree, dependencies, and full implementation below.

Component Architecture Call Tree
GoogleLine(Component Entry)
├──GoogleChartContainer[Lifecycle Shell]

Semantic <figure> providing glassmorphic loading spinner, error fallback, and accessibility tags.

├──google-chart-loader[Singleton Loader]

Async loader ensuring https://www.gstatic.com/charts/loader.js is fetched at most once across the application.

└──Google Visualization corechart[Hosted SVG Engine]

Google's LineChart class drawing vector paths onto the measured container div.

Involved Source Files & Registry Assets
registry/google/google-line.tsx
Primary component source file.
registry/google/google-chart-container.tsx
Loading, error, and frame container.
registry/google/google-chart-loader.ts
Deduplicated Google Charts package loader.
registry/google/google-line.tsx
"use client"import * as React from "react"import {  loadGoogleChartsPackages,  resolveGoogleColor,  escapeGoogleTooltipText,  type GoogleChartsLoaderState,  type GoogleVisualizationChart,} from "./google-chart-loader"import { GoogleChartContainer } from "./google-chart-container"export interface GoogleLineDatum {  label: string  value: number  [key: string]: unknown}export interface GoogleLineProps {  data: GoogleLineDatum[]  valueKey?: string  labelKey?: string  color?: string  height?: number | string  curveType?: "function" | "none"  pointSize?: number  showGrid?: boolean  loading?: boolean  error?: string | null  className?: string}export function GoogleLine({  data,  valueKey = "value",  labelKey = "label",  color = "var(--chart-1)",  height = 320,  curveType = "function",  pointSize = 0,  showGrid = true,  loading = false,  error = null,  className,}: GoogleLineProps) {  const chartRef = React.useRef<HTMLDivElement>(null)  const chartInstanceRef = React.useRef<GoogleVisualizationChart | null>(null)  const [loaderStatus, setLoaderStatus] = React.useState<GoogleChartsLoaderState>("loading")  React.useEffect(() => {    loadGoogleChartsPackages(["corechart"])      .then(() => setLoaderStatus("ready"))      .catch(() => setLoaderStatus("error"))  }, [])  const effectiveStatus: GoogleChartsLoaderState = loading    ? "loading"    : error      ? "error"      : loaderStatus  const drawChart = React.useCallback(() => {    if (effectiveStatus !== "ready" || !chartRef.current || !window.google?.visualization) return    const css = getComputedStyle(chartRef.current)    const resolvedColor = resolveGoogleColor(color, chartRef.current, "#f4f4f5")    const axisColor = css.getPropertyValue("--chart-axis").trim() || "#a1a1aa"    const gridColor = css.getPropertyValue("--chart-grid").trim() || "rgba(255,255,255,0.08)"    const dataTable = new window.google.visualization.DataTable()    dataTable.addColumn("string", "Label")    dataTable.addColumn("number", "Value")    dataTable.addColumn({ type: "string", role: "tooltip", p: { html: true } })    data.forEach((item) => {      const label = String(item[labelKey])      const safeLabel = escapeGoogleTooltipText(label)      const val = Number(item[valueKey])      const displayVal = Number.isFinite(val) ? val.toLocaleString() : String(item[valueKey])      const tooltipHtml = `        <div class="plotcn-tooltip-card">          <div class="plotcn-tooltip-header">            <span class="plotcn-tooltip-indicator" style="background-color: ${resolvedColor};"></span>            <span class="plotcn-tooltip-title">${safeLabel}</span>          </div>          <div class="plotcn-tooltip-metric">            <span class="plotcn-tooltip-label">${valueKey}</span>            <span class="plotcn-tooltip-value">${displayVal}</span>          </div>        </div>      `.trim()      dataTable.addRow([label, val, tooltipHtml])    })    const options = {      backgroundColor: "transparent",      colors: [resolvedColor],      curveType: curveType,      pointSize: pointSize,      legend: "none",      hAxis: {        textStyle: { color: axisColor, fontSize: 11 },        baselineColor: gridColor,        gridlines: { color: "transparent" },      },      vAxis: {        textStyle: { color: axisColor, fontSize: 11 },        baselineColor: gridColor,        gridlines: { color: showGrid ? gridColor : "transparent" },      },      tooltip: {        isHtml: true,      },      chartArea: { width: "85%", height: "75%" },    }    if (!chartInstanceRef.current) {      chartInstanceRef.current = new window.google.visualization.LineChart(chartRef.current)    }    chartInstanceRef.current.draw(dataTable, options)  }, [effectiveStatus, data, labelKey, valueKey, color, curveType, pointSize, showGrid])  // Redraw chart on prop update  React.useEffect(() => {    drawChart()  }, [drawChart])  // ResizeObserver for responsive redraw  React.useEffect(() => {    if (!chartRef.current) return    let frame = 0    const observer = new ResizeObserver(() => {      cancelAnimationFrame(frame)      frame = requestAnimationFrame(drawChart)    })    observer.observe(chartRef.current)    return () => {      observer.disconnect()      cancelAnimationFrame(frame)    }  }, [drawChart])  // Unmount cleanup ONLY  React.useEffect(() => {    return () => {      chartInstanceRef.current?.clearChart?.()      chartInstanceRef.current = null    }  }, [])  React.useEffect(() => {    const surface = chartRef.current?.closest("[data-theme]")    const observer = new MutationObserver(drawChart)    if (surface) observer.observe(surface, { attributes: true, attributeFilter: ["data-theme"] })    if (typeof document !== "undefined") {      observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class", "data-theme"] })    }    const media = window.matchMedia("(prefers-color-scheme: dark)")    media.addEventListener("change", drawChart)    return () => { observer.disconnect(); media.removeEventListener("change", drawChart) }  }, [drawChart])  return (    <GoogleChartContainer      status={effectiveStatus}      errorMessage={typeof error === "string" ? error : undefined}      height={height}      className={className}      chartRef={chartRef}      title="Google Line Chart"    >      <span className="sr-only">Line chart rendered with Google Charts corechart package.</span>    </GoogleChartContainer>  )}