Semiotic provides responsive wrapper components that automatically resize visualizations to fit their container. Instead of hardcoding pixel dimensions, you can let the chart fill available space — essential for dashboards, articles, and any layout where the container width is not known ahead of time.
With Charts
Chart components like LineChart andBarChart support responsive behavior through theframeProps escape hatch. Wrap the underlying frame configuration with responsive settings, or use the responsive Frame components directly:
JSX
import { LineChart } from "semiotic" // Option 1: Use frameProps to pass responsive settings <div style={{ width: "100%" }}> <LineChart data={salesData} xAccessor="month" yAccessor="revenue" xLabel="Month" yLabel="Revenue" size={[800, 400]} frameProps={{ responsiveWidth: true }} /> </div>
Alternatively, use the StreamXYFrame directly for full control:
JSX
import { StreamXYFrame } from "semiotic" <div style={{ width: "100%", height: 400 }}> <StreamXYFrame data={[{ coordinates: salesData }]} chartType="line" lineDataAccessor="coordinates" xAccessor="month" yAccessor="revenue" lineStyle={{ stroke: "#6366f1", strokeWidth: 2 }} showAxes={true} xLabel="Revenue" yLabel="Month" responsiveWidth={true} size={[800, 400]} /> </div>
With Frames
StreamXYFrame (Responsive)
StreamXYFrame supports responsive behavior throughresponsiveWidth and/or responsiveHeight props. The size prop still provides the initial dimensions and aspect ratio.
Responsive Width (resize your browser to see it adapt)
import { StreamXYFrame } from "semiotic" const frameProps = { /* --- Data --- */ data: [{ label: "Revenue", coordinates: [ { step: 1, value: 12000 }, { step: 2, value: 18000 }, // ...more coordinates ] }], chartType: "line", /* --- Size --- */ size: [600,300], margin: { top: 20, bottom: 60, left: 70, right: 20 }, responsiveWidth: true, /* --- Process --- */ xAccessor: "step", yAccessor: "value", lineDataAccessor: "coordinates", /* --- Customize --- */ lineStyle: { stroke: "#6366f1", strokeWidth: 2 }, showAxes: true, xLabel: "Month", yLabel: "Revenue ($)" } export default () => { return <StreamXYFrame {...frameProps} /> }
StreamOrdinalFrame
StreamOrdinalFrame works the same way for bar charts, swarm plots, and other ordinal visualizations:
import { StreamOrdinalFrame } from "semiotic" const frameProps = { /* --- Data --- */ data: [ { category: "Q1", revenue: 24000 }, { category: "Q2", revenue: 31000 }, { category: "Q3", revenue: 28000 }, { category: "Q4", revenue: 36000 } ], chartType: "bar", /* --- Size --- */ margin: { top: 20, bottom: 60, left: 80, right: 20 }, /* --- Process --- */ oAccessor: "category", rAccessor: "revenue", /* --- Customize --- */ pieceStyle: () => ({ fill: "#6366f1", stroke: "white" }), showAxes: true } export default () => { return <StreamOrdinalFrame {...frameProps} /> }
Responsive Height
Set responsiveHeight to true to make the chart height match its container. This requires the container to have an explicit height set (via CSS or inline styles):
JSX
// Container must have explicit height for responsiveHeight to work <div style={{ width: "100%", height: "50vh" }}> <StreamXYFrame data={data} chartType="line" lineDataAccessor="coordinates" responsiveWidth={true} responsiveHeight={true} size={[800, 400]} xAccessor="step" yAccessor="value" /> </div>
Configuration
Available Responsive Components
| Responsive Component | Wraps |
|---|
| StreamXYFrame | StreamXYFrame (replaces StreamXYFrame + StreamXYFrame) |
| StreamOrdinalFrame | StreamOrdinalFrame |
| StreamNetworkFrame | StreamNetworkFrame |
Responsive Props
| Prop | Type | Required | Default | Description |
|---|
responsiveWidth | boolean | — | false | When true, the frame width (size[0]) automatically matches the container width. |
responsiveHeight | boolean | — | false | When true, the frame height (size[1]) automatically matches the container height. |
Usage Patterns
JSX
import { StreamXYFrame, StreamOrdinalFrame, StreamNetworkFrame } from "semiotic" // Width-only responsive (most common) <StreamXYFrame responsiveWidth={true} size={[800, 400]} // Height stays fixed at 400 {...otherProps} /> // Both width and height responsive <div style={{ width: "100%", height: "60vh" }}> <StreamXYFrame responsiveWidth={true} responsiveHeight={true} size={[800, 400]} // Used as initial/fallback dimensions {...otherProps} /> </div> // Height-only responsive (rare) <div style={{ width: 600, height: "100%" }}> <StreamOrdinalFrame responsiveHeight={true} size={[600, 400]} // Width stays fixed at 600 {...otherProps} /> </div>
Best Practices
- Always provide a
size prop — it serves as the initial dimension and fallback before the container is measured. - Set explicit container height for
responsiveHeight— the container must have a defined height (via CSS, vh units, or flex/grid layout) for height-based responsiveness to work. - Margins remain fixed — only the chart drawing area scales. Margins stay at their configured pixel values, ensuring axis labels and padding remain readable.
- Debouncing — Responsive components internally debounce resize events to avoid excessive re-renders during window resizing.
- Combine with CSS Grid or Flexbox — for dashboard layouts, place responsive frames inside grid/flex containers that manage the available space.
JSX
/* Dashboard layout with responsive charts */ .dashboard { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; } .chart-card { min-height: 300px; } /* Each StreamXYFrame fills its card */ <div className="dashboard"> <div className="chart-card"> <StreamXYFrame responsiveWidth={true} size={[400, 300]} ... /> </div> <div className="chart-card"> <StreamOrdinalFrame responsiveWidth={true} size={[400, 300]} ... /> </div> </div>
- StreamXYFrame — the canvas-first XY frame with built-in responsive support
- StreamOrdinalFrame — the base frame that StreamOrdinalFrame wraps
- StreamNetworkFrame — the base frame that StreamNetworkFrame wraps
- Axes — axis configuration remains fixed while the chart area scales
- Accessibility — ensuring responsive charts remain accessible