Skip to content

The interactivity ladder

RungWhatHowCosts
1 · StaticReport and landing pagesAstro + PatternFly React rendered server-side + SVG chartsZero client JS from PatternFly; the page script is Astro’s prefetch
2 · IslandAn interactive chart, a filter, a small map inside a report or doc<Chart interactive> or any React component with client:visibleThat component’s JS only; PatternFly React components import their own CSS
3 · Page-appA micro-tool at /tools/<name>client:only="react" React app on an Astro page (see /tools/example) — same chrome, theme, bundles, deployReact runtime + your app; state in the URL hash for deep links
4 · StandaloneA tool that needs its own lifecycle, auth or SSRapps/tools/<name> Vite + React + PatternFly React consuming @csdr/theme/chromedeferred to v2A second deployable
// src/components/tools/MyIsland.tsx — allowlisted PatternFly components only
import { Toolbar, ToolbarContent, ToolbarItem } from '@patternfly/react-core/dist/esm/components/Toolbar/index.js';
import { Slider } from '@patternfly/react-core/dist/esm/components/Slider/index.js';
export default function MyIsland() { … }
---
import MyIsland from '../components/tools/MyIsland';
---
<MyIsland client:visible />

For charts inside islands import echarts dynamically and register the themes: const { registerCsdrThemes, themeName } = await import('@csdr/charts/theme'), then echarts.init(el, themeName(mode, document.documentElement.dataset.palette)) so the chart follows the page’s theme and palette (data-theme / data-palette on <html>, D13). ChartIsland in @csdr/charts does this for you when you already have an option object.

Static hosting has no server routes under /tools/<name>/, so tools keep their state in the hash (#from=1990&to=2026) or use run_worker_first in wrangler.jsonc if a tool must own path segments.