Skip to content
Plotcnbeta
DocsChartsBlocksPlaygroundThemesExamples
Get started
Guide
  • Introduction
  • System Design
  • Engine Strategy
  • Installation
  • Project Setup
  • shadcn/ui Setup
  • Plotcn Registry
Fundamentals
  • Usage
  • Theming
  • Accessibility
  • Motion & Animation
  • Performance
  • TypeScript & I18n
Google Charts
  • Google Charts
  • Google GeoChart
Docs/Fundamentals/Usage

Usage

Fundamentals

Build with Plotcn.

Install a visualization, import the source into your application, connect it to your data, and customize the implementation as deeply as your product requires.

ReactTypeScriptSource-ownedEngine-awareComposable
Local Source Import Contractzero runtime wrapper
import { PlotLineChart } from "@/components/charts/plot-line-chart"
Lifecycle Model

Plotcn Visualization Lifecycle

The 6-step developer workflow from source installation to production deployment.

  1. 01Done
    Install Source

    Run shadcn add @plotcn/<item> to copy pure TypeScript into your application.

  2. 02Done
    Import Locally

    Import the source component from @/components/charts/ using project aliases.

  3. 03Done
    Pass Typed Data

    Feed strongly-typed application data arrays matching the component contract.

  4. 04Active
    Responsive Shell

    Render inside an explicit height container (e.g. h-[320px] or aspect-video).

  5. 05Active
    Customize Locally

    Adjust SVG geometry, tooltips, axis formats, or animations directly in source.

  6. 06Done
    Ship to Production

    Compile standard React code with zero third-party Plotcn runtime lock-in.

Basic usage

Working with an installed Plotcn component is identical to working with any local React component in your codebase. Once installed via the CLI, you import the source file, provide your data array, and place the component inside your page layout.

Here is a minimal, complete example using the Cartesian line chart (PlotLineChart):

app/dashboard/revenue-chart.tsx
import { PlotLineChart, type PlotDatum } from "@/components/charts/plot-line-chart"const revenueData: PlotDatum[] = [  { label: "Jan", value: 12400, previous: 10200 },  { label: "Feb", value: 14800, previous: 11400 },  { label: "Mar", value: 16200, previous: 13100 },  { label: "Apr", value: 15900, previous: 14500 },  { label: "May", value: 18400, previous: 15200 },  { label: "Jun", value: 21200, previous: 17800 },]export function RevenueChart() {  return (    <div className="h-[280px] w-full">      <PlotLineChart        data={revenueData}        label="Monthly Revenue"      />    </div>  )}

Local source

Install source once. Use it like any other local React component. After a Plotcn component is installed, there is no special runtime API or provider you must initialize. Import the local source file, supply the required props, and render it anywhere in your component tree.
++

Importing a component

Plotcn visualizations are distributed directly into your project's components/charts/ folder. The import statement resolves to your local filesystem using your configured TypeScript path aliases.

TSX
import { PlotLineChart } from "@/components/charts/plot-line-chart"
Import Architecture

Source Path Resolution Hierarchy

How your components.json configuration maps physical chart source files to clean application imports.

LOCAL FILE PATH
Direct Source
components/charts/recharts/plot-line-chart.tsx
Component source file stored directly in your Git repository.
Local File
SHARED PRIMITIVE
Shared Shell
components/charts/shared/chart-container.tsx
ResizeObserver container & CSS theme variable observer.
Local File
resolves via @/* alias
resolves via @/* alias
APPLICATION PAGE / COMPONENT
Root Alias
import { PlotLineChart } from "@/components/charts/..."
Clean root-relative import without fragile relative paths (../../). Zero barrel files, zero runtime proxies.
TypeScript TSX

Never import from a "plotcn" package

Plotcn does not publish a heavyweight npm runtime package. Do not write:

TSX
// ❌ Incorrect: Plotcn is not an npm runtime packageimport { PlotLineChart } from "plotcn"

Because the source lives in your repository, you maintain full control over imports, file locations, bundle splitting, and renaming. If you reorganize your project folders, update your local import path accordingly.

++

Passing data

Visualization contracts should always be strongly typed. Avoid loose Record<string, any> objects or untyped arrays that obscure data bugs until runtime.

Explicit data contracts

types/chart-data.ts
export type RevenuePoint = {  label: string     // Independent axis (month, category, date)  value: number     // Primary metric  previous?: number // Optional comparison benchmark}

Then supply an array matching that contract:

TSX
const data: RevenuePoint[] = [  { label: "Q1", value: 45000, previous: 38000 },  { label: "Q2", value: 52000, previous: 44000 },]<PlotLineChart data={data} label="Quarterly Performance" />

Typed Data Principle

Prefer explicit data interfaces over generic object bags. Strongly typed data models ensure autocomplete for series keys, prevent typos in accessor functions, and guarantee that nullish or NaN numeric values are caught before rendering.

Plotcn does not own your data-fetching layer

Plotcn components are pure presentation leaves. They do not dictate how your data is fetched, cached, or refreshed. Feed your charts using whichever pattern fits your architecture:

  • React Server Components: Fetch data in page.tsx and pass serialized arrays down.
  • TanStack Query / SWR: Consume client hooks with automatic revalidation.
  • Route Loaders: Provide data through Next.js or React Router loaders.
++

Sizing and responsiveness

Visualizations do not force hardcoded pixel dimensions. Instead, they dynamically scale to fill the width and height of their immediate parent container.

Parent Height Contract

Responsive Sizing & Container Hierarchy

Visualizations dynamically fill the parent box. Always reserve vertical space with an explicit height.

  1. 01Done
    Parent Container

    Application sets explicit height: <div className="h-[320px] w-full">.

  2. 02Done
    ChartContainer Shell

    Observes parent box dimensions via native ResizeObserver or SVG viewBox.

  3. 03Active
    Measured Dimensions

    Width: 640px, Height: 320px non-zero dimensions computed for coordinate scales.

  4. 04Done
    Crisp SVG Marks

    Lines, grids, areas, and tooltips render pixel-perfect vectors with zero layout shift.

Sizing container contract

To ensure proper rendering and responsive recalculations, always wrap the chart in a container with an explicit height:

TSX
{/* Explicit height parent using Tailwind CSS */}<div className="h-[320px] w-full">  <PlotLineChart data={data} /></div>

You can use fixed pixel heights (h-[360px]), Tailwind scale utilities (h-64, h-80), or responsive aspect ratios (aspect-[16/9], aspect-video).

Avoid Zero-Height Containers

A responsive chart cannot render into a container with no measurable height. If the parent container collapses to height: 0px (such as inside an unmeasured flex item or a closed tab panel), SVG geometry calculations will fail or emit ResizeObserver warnings. Always reserve vertical space.
++

Component states

In production applications, charts must handle four distinct operational states: loading, empty, error, and ready. Never display only the happy path.

Component Lifecycle State Machine

Truthful Representation
State: LoadingIn Flight
Evaluation Priority

Render an accessible skeleton or fallback with role='status'. Never render fake metrics or dummy graphs that mislead users.

if (isLoading) { return <div className="chart-fallback" role="status">Loading visualization…</div> }

State composition pattern

Implement truthful state branches using standard React conditionals or by passing state props:

components/analytics/active-users-card.tsx
import { PlotLineChart } from "@/components/charts/plot-line-chart"import { useQuery } from "@tanstack/react-query"export function ActiveUsersCard() {  const { data, isLoading, error } = useQuery({    queryKey: ["active-users"],    queryFn: fetchActiveUsers,  })  // 1. Error state (highest priority)  if (error) {    return (      <div className="h-[280px] flex items-center justify-center rounded-xl border border-rose-500/20 bg-rose-950/10 p-6 text-center text-xs text-rose-300">        Failed to load activity metrics. Please try again.      </div>    )  }  // 2. Loading state  if (isLoading) {    return (      <div className="h-[280px] flex items-center justify-center rounded-xl border border-white/[0.06] bg-zinc-900/30 text-xs text-zinc-500">        Loading visualization…      </div>    )  }  // 3. Empty state  if (!data?.length) {    return (      <div className="h-[280px] flex items-center justify-center rounded-xl border border-white/[0.06] bg-zinc-900/20 text-xs text-zinc-500">        No user activity recorded for this period.      </div>    )  }  // 4. Ready state  return (    <div className="h-[280px] w-full">      <PlotLineChart data={data} label="Active Users" />    </div>  )}

No fake data during loading

Never populate loading skeletons with random placeholder lines or simulated metrics. Faux data misleads users and can be mistaken for actual telemetry. Use truthful, neutral loading skeletons.

++

Configure the visualization

Plotcn components expose a concise, sensible public prop surface for common adjustments while keeping deep customization where it belongs: in your local source code.

Standard prop surface

TSX
<PlotLineChart  data={revenueData}  label="Monthly Revenue"  compact={false}  // Hide secondary axes for dense widgets  area={true}       // Enable soft gradient background fill  loading={false}   // Pass loading state directly  error={undefined} // Pass error message directly/>

Avoid Giant Prop APIs

Plotcn is source-owned, so every customization does not need a new prop. In traditional chart libraries, teams demand dozens of props (axisTickColor, tooltipBorderRadius, gradientStopOpacity) to configure minor visual details. In Plotcn, you simply edit the local source file directly.
++

Customize source

Because installed components belong to your repository, you can tailor them to your product's unique design system, backend requirements, and interaction patterns.

Example: Adding custom currency formatting

To format tooltip values as USD currency, locate the installed component file in components/charts/plot-line-chart.tsx and adjust the tick and tooltip formatters:

components/charts/plot-line-chart.tsx
// 1. Define your localized currency formatterconst formatCurrency = (val: number) =>  new Intl.NumberFormat("en-US", {    style: "currency",    currency: "USD",    maximumFractionDigits: 0,  }).format(val)// 2. Apply directly to YAxis ticks<YAxis  tickFormatter={(val) => `$${val / 1000}k`}  tick={{ fill: "var(--plot-muted)", fontSize: 11 }}/>// 3. Apply to Tooltip values<Tooltip  formatter={(value: number) => [formatCurrency(value), "Revenue"]}/>

The Golden Rule of Source Customization

  • Generic improvements: Keep layout containers, ARIA screen-reader labels, and theme variable references upstream-compatible.
  • Product-specific logic: Put brand-specific currency rules, custom SVG icons, and company domain logic directly in your local chart files.
++

Shared vs engine-specific APIs

A foundational Plotcn invariant is: "Consistency around the chart. Freedom inside the chart."

Shared Experience vs Engine-Specific APIs

Architectural Invariant

Plotcn unifies the developer experience around the chart—theming, responsive sizing, loading fallbacks, and accessibility—while preserving the raw power and idiomatic APIs of the underlying visualization engine.

ConcernShared StandardRechartsD3.jsGoogle Charts
Responsive ShellStandard container patternResponsiveContainerSVG viewBox aspect ratioGoogleChartContainer adapter
Loading / FallbackStandard skeleton statesLocal fallback shellLocal fallback shellScript loader status bridge
Theme SystemCSS tokens (--chart-1..5)Reads CSS variables directlyInterpolates CSS tokensColors array in options
Tooltip BehaviorAccessible conventionsCustom Recharts TooltipReact DOM overlayHTML tooltip option
Geometry MathEngine-specificRecharts Cartesian layoutd3-scale, d3-shape, d3-forceGoogle DataTable algorithms
Runtime LibraryNone (zero Plotcn runtime)recharts npm packageModular d3-* npm packagesGoogle CDN hosted runtime
Data TransformTyped application dataArray of recordsNormalized numeric seriesDataTable or 2D array

Plotcn standardizes the outer shell (theming tokens, container responsiveness, error boundaries, accessible screen-reader tables), but does not force Recharts, D3.js, and Google Charts into an artificial unified API. Each engine retains its native strengths.

++

Recharts

Recharts is the recommended choice for standard product dashboards, SaaS metrics, and declarative React composition.

Why Recharts?

  • Fastest implementation: Composable React elements (<AreaChart>, <XAxis>, <Tooltip>).
  • Declarative: Sizing and layout are handled seamlessly through React props.
  • Lightweight bundle: Only includes Cartesian layout logic.
components/charts/plot-line-chart.tsx
import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts"export function PlotLineChart({ data }: PlotLineChartProps) {  return (    <ResponsiveContainer width="100%" height="100%">      <AreaChart data={data}>        <XAxis dataKey="label" stroke="var(--chart-muted)" />        <YAxis stroke="var(--chart-muted)" />        <Tooltip />        <Area type="monotone" dataKey="value" stroke="currentColor" fill="url(#gradient)" />      </AreaChart>    </ResponsiveContainer>  )}

When you need to adjust grid dash arrays, margins, or active dot radiuses, you modify these JSX primitives directly.

++

D3.js

Plotcn approaches D3.js with a clear philosophy: D3 calculates the math and geometry; React renders the SVG elements.

Architectural Pattern

D3.js + React Separation of Concerns

D3 calculates the mathematics and layout geometry; React renders the SVG elements directly with native virtual DOM reconciliation.

01. D3 MATHEMATICS
Pure Math
D3 Micro-Modules
Modular packages (d3-shape, d3-scale) compute coordinates, angles, and curves.
Zero DOM Mutation
emits paths
emits paths
02. GEOMETRY STATE
Geometry Data
Computed Geometry Data
Pure serializable SVG path strings (M... Z) and coordinate arrays.
Serializable Array
consumed by JSX
consumed by JSX
03. VIRTUAL DOM RENDER
Native React
React JSX (<path />)
React renders SVG nodes with standard props, state, transitions, and SSR.
Zero d3.select() Hacks

Why avoid d3.select() in React?

Manipulating the DOM directly with d3.select().append("circle") breaks React's virtual DOM reconciliation, breaks server-side rendering, and makes component cleanup error-prone. By letting React render the SVG tags, you gain full access to React state, props, and transitions.

Example: Modular D3 path generation

components/charts/d3-donut.tsx
import { useMemo } from "react"import { pie, arc } from "d3-shape"export function D3Donut({ values }: { values: number[] }) {  const arcs = useMemo(() => {    const pieGen = pie<number>().sort(null).value(d => d)    const arcGen = arc<any>().innerRadius(60).outerRadius(90).cornerRadius(3)    return pieGen(values).map(slice => arcGen(slice))  }, [values])  return (    <svg viewBox="0 0 200 200" className="w-full h-full">      <g transform="translate(100, 100)">        {arcs.map((d, i) => (          <path key={i} d={d ?? ""} fill={`var(--chart-${(i % 5) + 1})`} />        ))}      </g>    </svg>  )}

Always use modular packages (d3-shape, d3-scale) rather than the umbrella d3 package to keep bundle sizes minimal.

++

Google Charts

Google Charts provides enterprise visualization types—most notably the Google GeoChart for interactive country and regional choropleth maps.

Hosted runtime considerations

Google Charts renders via a hosted Google CDN script (google.visualization). The Plotcn wrapper manages this external dependency gracefully:

  • Script deduplication: Injects the Google loader once and reuses the shared Promise.
  • Package isolation: Loads only the requested visualization package (e.g. geochart).
  • Responsive resize: Re-draws charts automatically on window resize.
app/dashboard/regional-traffic.tsx
import { GoogleGeoChart } from "@/components/charts/google/google-geochart"const countryTraffic = [  { region: "US", users: 12450 },  { region: "DE", users: 8920 },  { region: "IN", users: 14300 },  { region: "GB", users: 6710 },  { region: "JP", users: 9540 },]export function RegionalTraffic() {  return (    <div className="h-[380px] w-full">      <GoogleGeoChart        data={countryTraffic}        regionKey="region"        valueKey="users"        region="world"        displayMode="regions"      />    </div>  )}

Keep client boundaries narrow

Because Google Charts requires the browser DOM and external script execution, the component carries "use client". However, keep your parent route (page.tsx) as a Server Component and import the chart as an interactive client leaf.

++

Composition

Charts should integrate seamlessly into your broader product interface. Compose them inside standard dashboard surfaces:

app/dashboard/page.tsx
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card"import { PlotLineChart } from "@/components/charts/plot-line-chart"import { ChartBoundary } from "@/components/charts/chart-boundary"export default function AnalyticsDashboard({ revenueData }) {  return (    <div className="grid grid-cols-1 md:grid-cols-2 gap-6">      <Card>        <CardHeader>          <CardTitle>MRR Growth</CardTitle>          <CardDescription>Monthly recurring revenue over the past 6 months</CardDescription>        </CardHeader>        <CardContent>          <ChartBoundary>            <div className="h-[280px] w-full">              <PlotLineChart data={revenueData} label="MRR" />            </div>          </ChartBoundary>        </CardContent>      </Card>    </div>  )}

Unopinionated Components

A chart component should not assume it always lives inside a Card. Plotcn visualizations provide only the chart graphic and its direct accessibility wrappers. Your application dictates whether the chart sits inside a modal, card, full-width report, or slide-over drawer.
++

Interaction

Plotcn charts support rich user interaction while exposing clean, typed event callbacks to your application.

Strongly typed event handlers

TSX
<GoogleGeoChart  data={countryTraffic}  regionKey="region"  valueKey="users"  onRegionSelect={(regionCode: string) => {    console.log(`Selected region: ${regionCode}`)    router.push(`/analytics/regions/${regionCode.toLowerCase()}`)  }}/>

Event handler stability

Avoid creating inline anonymous functions for expensive event pipelines:

TSX
// ✅ Good: Stable callback referenceconst handleSelect = useCallback((region: string) => {  setSelectedRegion(region)}, [])<GoogleGeoChart onRegionSelect={handleSelect} />
++

Data transformation

Prepare application data before passing it to the visualization component. Keep business rules, pricing logic, and filtering outside of generic chart code.

Separation of concerns

TypeScript
// 1. Raw backend responseinterface OrderEntity {  id: string  totalCents: number  status: "completed" | "pending" | "refunded"  createdAt: string}// 2. Transformation utility outside componentfunction prepareRevenueData(orders: OrderEntity[]): PlotDatum[] {  return orders    .filter((o) => o.status === "completed")    .map((o) => ({      label: new Date(o.createdAt).toLocaleDateString("en-US", { month: "short" }),      value: o.totalCents / 100,    }))}

When to use useMemo

Only memoize transformations that process thousands of records or execute non-trivial D3 math (e.g. Voronoi tessellations, force simulations). For small arrays under 100 items, standard JavaScript array mappings are fast enough that useMemo introduces unnecessary overhead.

++

Performance

High-performance dashboard rendering relies on disciplined architectural practices:

  1. Keep Client Boundaries Narrow: Do not place "use client" at the route root. Keep data fetching on the server and render charts as isolated client components.
  2. Stable Dimensions: Always specify container heights to avoid cumulative layout shift (CLS) during chart mounting.
  3. Modular D3 Imports: Import only the specific submodules needed (d3-scale, d3-shape) to minimize JavaScript bundle size.
  4. Deduplicate External Loaders: Google Charts scripts are shared across all instances on a page through a single singleton promise.
  5. Contain Exceptions with ChartBoundary: Wrap complex visualizations in React error boundaries so a rendering error in one widget does not break the rest of the dashboard.
++

Common mistakes

Diagnostic guidance for common visualization integration pitfalls:

Common Pitfalls & Architectural Solutions

Diagnostic Checklist
Importing from Wrong Path

PROBLEM:Installed source exists in project but TypeScript compiler throws 'Cannot find module'.

CAUSE:Import statement uses incorrect alias or does not match components.json target paths.

FIX:Verify tsconfig.json paths '@/*' mapping and ensure import points to '@/components/charts/...'.
Zero-Height Container

PROBLEM:Chart appears blank, throws ResizeObserver loop limit errors, or collapses to 0px.

CAUSE:Parent layout lacks an explicit height (e.g. unmeasured flex container or collapsed accordion).

FIX:Set an explicit height class such as 'h-[320px] w-full' or 'aspect-video' on the parent element.
Passing Raw API Responses

PROBLEM:Chart becomes deeply coupled to backend database schemas and breaks on minor API changes.

CAUSE:Directly passing nested database entities instead of a focused, typed chart data array.

FIX:Transform API responses into an explicit data contract (e.g. PlotDatum[]) before passing to the chart.
Over-Generalizing Component Props

PROBLEM:A simple chart accumulates 35 configuration props to support every bespoke styling variation.

CAUSE:Treating installed Plotcn source like an inflexible third-party npm package.

FIX:Edit the local component source directly. Customize SVG defs, tick formatting, or margins in code.
Mixing Engine Dependencies

PROBLEM:Installing a Recharts component pulls D3 packages or causes bundle size bloat.

CAUSE:Importing an internal D3 helper inside a Recharts chart rather than using the shared layer.

FIX:Keep engine boundaries strict. Move cross-engine utilities into '@/components/charts/shared/'.
Making the Entire Route 'use client'

PROBLEM:Adding one chart causes the entire Next.js page, headers, and data loaders to run client-side.

CAUSE:Placing 'use client' at the top of page.tsx rather than isolating the interactive visualization.

FIX:Keep page.tsx as a React Server Component. Render the chart as a focused client component leaf.
Fake Loading Metrics

PROBLEM:Placeholder chart displays dummy values during data fetching that users mistake for real data.

CAUSE:Rendering a mock dataset during loading rather than a truthful loading skeleton or fallback.

FIX:Use a clean fallback state (role='status' with an animated skeleton) until real metrics resolve.
Hardcoding Hex Colors in SVG

PROBLEM:Chart looks crisp in dark mode but becomes illegible or invisible when toggled to light mode.

CAUSE:Hardcoding '#18181b' or '#fafafa' in SVG fill/stroke attributes instead of CSS tokens.

FIX:Use 'currentColor' or CSS variables like 'var(--chart-1)' through 'var(--chart-5)' for all marks.
++

Recommended workflow

Follow this 10-step sequence when building production visualizations with Plotcn:

Workflow Protocol

10-Step Production Development Workflow

The standard engineering sequence for building, styling, and shipping production visualizations with Plotcn.

  1. 01Done
    Choose Engine

    Recharts for dashboards, D3 for custom geometry, Google for GeoCharts.

  2. 02Done
    Install Source

    Run `shadcn add @plotcn/<chart>` to copy component source into your app.

  3. 03Done
    Inspect Code

    Review local source in @/components/charts/ to understand props and markup.

  4. 04Done
    Type Your Data

    Define explicit TypeScript interfaces for all data series and records.

  5. 05Active
    Set Layout Height

    Wrap chart in a container with measurable dimensions (e.g. h-[320px]).

  6. 06Active
    Handle States

    Implement loading, empty, and error fallback states explicitly.

  7. 07Active
    Customize Locally

    Adjust tooltips, axis tick formatting, and margins directly in source.

  8. 08Done
    Test Interactions

    Verify keyboard navigation, focus outlines, and tooltip hit targets.

  9. 09Done
    Verify Theming

    Confirm charts adapt seamlessly between dark and light color modes.

  10. 10Done
    Ship with Confidence

    Commit source into Git alongside the rest of your application code.

++

Next steps

Now that you understand day-to-day chart usage and integration patterns, explore styling and accessible rendering:

Installation

Prepare a React or Next.js project and install required dependencies.

shadcn/ui Setup

Configure components.json and integrate Plotcn with the shadcn Registry.

Registry Workflow

Learn how visualization components enter your local source tree.

PreviousPlotcn RegistryNextTheming

On this page

  • Basic usage
  • Importing a component
  • Passing data
  • Sizing and responsiveness
  • Component states
  • Configure the visualization
  • Customize source
  • Shared vs engine-specific APIs
  • Recharts
  • D3.js
  • Google Charts
  • Composition
  • Interaction
  • Data transformation
  • Performance
  • Common mistakes
  • Recommended workflow
  • Next steps