|
| 1 | +# Migrating to Semiotic v3 |
| 2 | + |
| 3 | +This guide covers upgrading from Semiotic v1.x or v2.x to v3. |
| 4 | + |
| 5 | +## Quick Summary |
| 6 | + |
| 7 | +| Area | Impact | |
| 8 | +|---|---| |
| 9 | +| Core Frame APIs | **No breaking changes** — `XYFrame`, `OrdinalFrame`, `NetworkFrame` props are unchanged | |
| 10 | +| React version | **React 18.1+** required (was 16+ in v1, 17+ in v2) | |
| 11 | +| Removed props | `baseMarkProps` removed from all Frames | |
| 12 | +| Removed components | `ProcessViz`, `Mark`, `SpanOrDiv` removed | |
| 13 | +| New features | 24 chart HOCs, RealtimeFrame, SSR, code splitting | |
| 14 | +| TypeScript | Built-in types ship with the package | |
| 15 | +| Bundle size | 62% smaller (minified), up to 78% smaller with code splitting | |
| 16 | + |
| 17 | +**For most users:** install v3 and your existing code works as-is. Then |
| 18 | +optionally adopt the new chart components for simpler code. |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Step 1: Update React |
| 23 | + |
| 24 | +Semiotic v3 requires React 18.1 or later. |
| 25 | + |
| 26 | +```bash |
| 27 | +npm install react@18 react-dom@18 |
| 28 | +``` |
| 29 | + |
| 30 | +If you are on React 16 or 17, you must upgrade first. See the |
| 31 | +[React 18 upgrade guide](https://react.dev/blog/2022/03/08/react-18-upgrade-guide). |
| 32 | + |
| 33 | +## Step 2: Install Semiotic v3 |
| 34 | + |
| 35 | +```bash |
| 36 | +npm install semiotic@3 |
| 37 | +``` |
| 38 | + |
| 39 | +## Step 3: Check for Removed APIs |
| 40 | + |
| 41 | +### `baseMarkProps` (removed) |
| 42 | + |
| 43 | +**Before (v1/v2):** |
| 44 | +```jsx |
| 45 | +<XYFrame |
| 46 | + baseMarkProps={{ transitionDuration: { fill: 500 } }} |
| 47 | + // ... |
| 48 | +/> |
| 49 | +``` |
| 50 | + |
| 51 | +**After (v3):** |
| 52 | +Use `lineStyle`, `pointStyle`, or `summaryStyle` props instead: |
| 53 | +```jsx |
| 54 | +<XYFrame |
| 55 | + lineStyle={{ transition: "fill 500ms" }} |
| 56 | + // ... |
| 57 | +/> |
| 58 | +``` |
| 59 | + |
| 60 | +### `ProcessViz` (removed) |
| 61 | + |
| 62 | +This was a development/debugging component. Remove any imports: |
| 63 | +```diff |
| 64 | +- import { ProcessViz } from "semiotic" |
| 65 | +``` |
| 66 | + |
| 67 | +### `Mark` component (removed) |
| 68 | + |
| 69 | +Use direct SVG elements: |
| 70 | +```diff |
| 71 | +- import { Mark } from "semiotic" |
| 72 | +- <Mark markType="rect" width={10} height={20} /> |
| 73 | ++ <rect width={10} height={20} /> |
| 74 | +``` |
| 75 | + |
| 76 | +## Step 4: Adopt New Features (Optional) |
| 77 | + |
| 78 | +### Use Chart Components Instead of Frames |
| 79 | + |
| 80 | +The biggest v3 addition is 24 chart components with simplified APIs. These |
| 81 | +are optional — Frames continue to work — but they dramatically reduce code |
| 82 | +for common chart types. |
| 83 | + |
| 84 | +**Before (Frame):** |
| 85 | +```jsx |
| 86 | +import { XYFrame } from "semiotic" |
| 87 | + |
| 88 | +<XYFrame |
| 89 | + lines={[{ coordinates: salesData }]} |
| 90 | + xAccessor="month" |
| 91 | + yAccessor="revenue" |
| 92 | + lineDataAccessor="coordinates" |
| 93 | + lineType={{ type: "line", interpolator: curveMonotoneX }} |
| 94 | + showLinePoints={true} |
| 95 | + pointStyle={{ fill: "#6366f1", r: 3 }} |
| 96 | + lineStyle={{ stroke: "#6366f1", strokeWidth: 2 }} |
| 97 | + axes={[ |
| 98 | + { orient: "left", label: "Revenue" }, |
| 99 | + { orient: "bottom", label: "Month" }, |
| 100 | + ]} |
| 101 | + hoverAnnotation={true} |
| 102 | + size={[600, 400]} |
| 103 | +/> |
| 104 | +``` |
| 105 | + |
| 106 | +**After (Chart):** |
| 107 | +```jsx |
| 108 | +import { LineChart } from "semiotic" |
| 109 | + |
| 110 | +<LineChart |
| 111 | + data={salesData} |
| 112 | + xAccessor="month" |
| 113 | + yAccessor="revenue" |
| 114 | + curve="monotoneX" |
| 115 | + showPoints={true} |
| 116 | + xLabel="Month" |
| 117 | + yLabel="Revenue" |
| 118 | +/> |
| 119 | +``` |
| 120 | + |
| 121 | +Both approaches continue to work. Use `frameProps` on any chart component |
| 122 | +to access the full Frame API without giving up the simpler interface: |
| 123 | + |
| 124 | +```jsx |
| 125 | +<LineChart |
| 126 | + data={salesData} |
| 127 | + xAccessor="month" |
| 128 | + yAccessor="revenue" |
| 129 | + frameProps={{ |
| 130 | + annotations: [{ type: "x", month: 6, label: "Mid-year" }], |
| 131 | + }} |
| 132 | +/> |
| 133 | +``` |
| 134 | + |
| 135 | +**Available chart components:** |
| 136 | + |
| 137 | +| Category | Components | |
| 138 | +|---|---| |
| 139 | +| XY | `LineChart`, `AreaChart`, `StackedAreaChart`, `Scatterplot`, `BubbleChart`, `Heatmap` | |
| 140 | +| Ordinal | `BarChart`, `StackedBarChart`, `GroupedBarChart`, `SwarmPlot`, `BoxPlot`, `DotPlot`, `PieChart`, `DonutChart` | |
| 141 | +| Network | `ForceDirectedGraph`, `ChordDiagram`, `SankeyDiagram`, `TreeDiagram`, `Treemap`, `CirclePack` | |
| 142 | +| Realtime | `RealtimeLineChart`, `RealtimeBarChart`, `RealtimeSwarmChart`, `RealtimeWaterfallChart` | |
| 143 | + |
| 144 | +### Use Granular Imports for Smaller Bundles |
| 145 | + |
| 146 | +If you only use one type of visualization, import from the specific entry |
| 147 | +point to reduce your bundle by 36-43%: |
| 148 | + |
| 149 | +```diff |
| 150 | +- import { XYFrame, LineChart } from "semiotic" // 218 KB |
| 151 | ++ import { XYFrame, LineChart } from "semiotic/xy" // 125 KB |
| 152 | +``` |
| 153 | + |
| 154 | +```diff |
| 155 | +- import { OrdinalFrame, BarChart } from "semiotic" // 218 KB |
| 156 | ++ import { OrdinalFrame, BarChart } from "semiotic/ordinal" // 140 KB |
| 157 | +``` |
| 158 | + |
| 159 | +```diff |
| 160 | +- import { NetworkFrame } from "semiotic" // 218 KB |
| 161 | ++ import { NetworkFrame } from "semiotic/network" // 133 KB |
| 162 | +``` |
| 163 | + |
| 164 | +Each granular entry point includes the relevant Frame, its chart components, |
| 165 | +and shared utilities (Axis, Legend, Annotation, Brush). |
| 166 | + |
| 167 | +### Use Server-Side Rendering |
| 168 | + |
| 169 | +For static SVG generation (email, OG images, PDF, static sites): |
| 170 | + |
| 171 | +```jsx |
| 172 | +import { renderToStaticSVG } from "semiotic/server" |
| 173 | + |
| 174 | +const svg = renderToStaticSVG("xy", { |
| 175 | + lines: [{ coordinates: data }], |
| 176 | + xAccessor: "date", |
| 177 | + yAccessor: "value", |
| 178 | + size: [600, 400], |
| 179 | + axes: [{ orient: "left" }, { orient: "bottom" }], |
| 180 | +}) |
| 181 | + |
| 182 | +// svg is a string of static SVG markup |
| 183 | +``` |
| 184 | + |
| 185 | +### Use RealtimeFrame for Streaming Data |
| 186 | + |
| 187 | +For high-frequency data (monitoring, IoT, financial): |
| 188 | + |
| 189 | +```jsx |
| 190 | +import { RealtimeLineChart } from "semiotic" |
| 191 | + |
| 192 | +const chartRef = useRef() |
| 193 | + |
| 194 | +// Push data imperatively |
| 195 | +useEffect(() => { |
| 196 | + const interval = setInterval(() => { |
| 197 | + chartRef.current.push({ time: Date.now(), value: Math.random() }) |
| 198 | + }, 100) |
| 199 | + return () => clearInterval(interval) |
| 200 | +}, []) |
| 201 | + |
| 202 | +<RealtimeLineChart |
| 203 | + ref={chartRef} |
| 204 | + timeAccessor="time" |
| 205 | + valueAccessor="value" |
| 206 | + windowSize={200} |
| 207 | + size={[600, 300]} |
| 208 | +/> |
| 209 | +``` |
| 210 | + |
| 211 | +## Step 5: TypeScript (Optional) |
| 212 | + |
| 213 | +Semiotic v3 ships its own type definitions. Remove any community types: |
| 214 | + |
| 215 | +```diff |
| 216 | +- npm uninstall @types/semiotic |
| 217 | +``` |
| 218 | + |
| 219 | +All components support generics: |
| 220 | + |
| 221 | +```tsx |
| 222 | +import { LineChart, LineChartProps } from "semiotic" |
| 223 | + |
| 224 | +interface SalesPoint { |
| 225 | + month: number |
| 226 | + revenue: number |
| 227 | +} |
| 228 | + |
| 229 | +<LineChart<SalesPoint> |
| 230 | + data={salesData} |
| 231 | + xAccessor="month" // TypeScript validates this is a key of SalesPoint |
| 232 | + yAccessor="revenue" |
| 233 | +/> |
| 234 | +``` |
| 235 | + |
| 236 | +## Step 6: Next.js / SSR Frameworks |
| 237 | + |
| 238 | +Semiotic v3 includes `"use client"` directives on all interactive components. |
| 239 | +No special configuration is needed for Next.js App Router, Remix, or other |
| 240 | +React Server Component frameworks. |
| 241 | + |
| 242 | +```jsx |
| 243 | +// app/dashboard/page.tsx (Next.js App Router) |
| 244 | +import { LineChart } from "semiotic" |
| 245 | + |
| 246 | +// Works — LineChart is marked "use client" internally |
| 247 | +export default function DashboardPage() { |
| 248 | + return <LineChart data={data} xAccessor="x" yAccessor="y" /> |
| 249 | +} |
| 250 | +``` |
| 251 | + |
| 252 | +--- |
| 253 | + |
| 254 | +## FAQ |
| 255 | + |
| 256 | +### Do I need to change my existing Frame code? |
| 257 | + |
| 258 | +No. `XYFrame`, `OrdinalFrame`, and `NetworkFrame` have the same prop API as |
| 259 | +v1/v2. Your existing code should work without changes (minus the three |
| 260 | +removed features listed above). |
| 261 | + |
| 262 | +### Should I switch from Frames to Chart components? |
| 263 | + |
| 264 | +For new code, yes — the chart components handle data transformation, axis |
| 265 | +configuration, legends, and hover interactions automatically. For existing |
| 266 | +Frame code that works well, there is no urgency to migrate. |
| 267 | + |
| 268 | +### Can I mix Frames and Chart components? |
| 269 | + |
| 270 | +Yes. They are independent components that can coexist on the same page. |
| 271 | + |
| 272 | +### What happened to v2? |
| 273 | + |
| 274 | +Version 2 was a series of release candidates (up to `2.0.0-rc.12`) that |
| 275 | +began the internal refactoring to functional components and TypeScript. It |
| 276 | +was never promoted to a stable release. v3 completes that work and adds |
| 277 | +the chart components, RealtimeFrame, SSR, and code splitting. |
0 commit comments