ChordSketch

# @chordsketch/react React component library for embedding [ChordPro](https://www.chordpro.org/) **and** [iReal Pro](https://www.irealpro.com/) editors + previews in a few lines of React, powered by [`@chordsketch/wasm`](https://www.npmjs.com/package/@chordsketch/wasm). `@chordsketch/react@0.3.0` consolidates the component surface into three explicit tiers (#2527 / #2533) and removes the ambiguous "Editor" suffix from Tier 1 atoms. Tier 3 composed editors are now the only components that carry an "Editor" suffix. ChordPro and iReal Pro both expose the same three-tier shape so consumers can choose the surface that matches their host. ## Component layout (three tiers) | Tier | Purpose | ChordPro | iReal Pro | |------|---------|----------|-----------| | **Tier 1 atoms** | Single-responsibility primitives | ``, ``, ``, ``, ``, ``, ``, `` | ``, `` | | **Tier 2 preview-with-controls** | Preview surface with built-in format / transpose controls — host owns the source | `` | — (use `` directly) | | **Tier 3 composed editor** | Opinionated all-in-one editor + preview shell | `` | `` | ### Consumer-to-tier mapping `` and `` are the **recommended Tier 3 all-in-one surfaces** for external integrators — they ship the playground / desktop UX out of the box and are the right default for most embedders. The in-repo playground and the Tauri desktop app deliberately compose Tier 1 / Tier 2 components into app-specific layouts so they can own their own chrome (page routing in the playground; Tauri menu + tree-sitter editor in the desktop). External consumers without those constraints should reach for the Tier 3 components first. | Consumer | ChordPro components | iReal Pro components | |----------|---------------------|----------------------| | Playground page (this repo) | Composes Tier 1 atoms (``, ``, ...) into a custom layout | Composes the iReal Pro atoms similarly | | Tauri desktop app | `` (Tier 2) + a local `` (CodeMirror 6 + `tree-sitter-chordpro`) | `` (Tier 1) + a local `` wrapping `@chordsketch/ui-irealb-editor` | | VS Code WebView preview | `` (Tier 2) | `` (Tier 1) | | External React consumers (recommended) | `` (Tier 3) — opinionated all-in-one | `` (Tier 3) — opinionated all-in-one | | External React consumers (custom layout) | Compose Tier 1 atoms (`` / `` + `` or ``) | Compose `` + `` | Tier 1 atoms never carry an "Editor" suffix — they are single-responsibility primitives. `` does include a built-in preview pane (the "Textarea" name reflects the editor surface technology, not the absence of a preview); `` is the CodeMirror-backed source-edit surface without a preview. `` is the iReal Pro bar-grid editor surface alone. Tier 3 composed editors (``, ``) are the only components whose name carries an "Editor" suffix; they each compose multiple Tier 1 / Tier 2 surfaces into the opinionated all-in-one shell. ## Installation [![npm](https://img.shields.io/npm/v/@chordsketch/react)](https://www.npmjs.com/package/@chordsketch/react) Replace `VERSION` with the current version from the badge above. ```bash npm install '@chordsketch/react@VERSION' react react-dom ``` `@chordsketch/wasm` is declared as a regular `dependency`, so npm installs it automatically as a transitive dependency of `@chordsketch/react`; the wasm module is then lazy-loaded on first render. Hosts do not install it separately and do not need to call `init()`. `react` / `react-dom` are **peer dependencies** (React 18 or newer). The PDF / PNG export bundle ships separately as the heavy `@chordsketch/wasm-export` peer (~6 MB gzipped). Install it alongside this package **only** if you use the `` / `usePdfExport` surface; it is lazy-loaded the first time you call the export. ```bash # Optional — only needed for / usePdfExport. npm install @chordsketch/wasm-export ``` ### Peer dependency compatibility | Peer | Required range | Notes | |------|----------------|-------| | `react` | `>=18` | Both 18.x and 19.x are supported. | | `react-dom` | `>=18` | Track the `react` major. | | `@chordsketch/wasm` | `^0.5.0` (runtime dep) | Bundled as a regular dependency; hosts can override at hoist time if they want a specific minor. | | `@chordsketch/wasm-export` | `^0.5.0` (optional peer) | Required for `` / `usePdfExport`. Lazy-loaded on first export. | ### Platform compatibility | Platform | Status | |---|---| | Browsers (evergreen Chromium / Firefox / Safari) | Supported — uses the `web` build of `@chordsketch/wasm`. | | Node.js 18+ (SSR) | Renderer hooks work via the `node` build of `@chordsketch/wasm`. Editor components mount on the client (`'use client'` boundary in Next.js — see [Next.js notes](#nextjs--ssr) below). | | Bun / Deno | Best-effort — both expose the Node.js `import('@chordsketch/wasm')` entry, but no CI coverage today. | | React Native / Hermes | Not supported — depends on the browser / Node WebAssembly loaders. | ## Usage ### `` — flagship render component ```tsx import { ChordSheet } from '@chordsketch/react'; import '@chordsketch/react/styles.css'; const source = `{title: Amazing Grace} {key: G} [G]Amazing [G7]grace, how [C]sweet the [G]sound`; export function Sheet() { return ; } ``` `format="html"` (default) parses the ChordPro source via `@chordsketch/wasm`'s `parseChordpro` export and walks the AST into a React tree directly through the `chordpro-jsx` walker. No HTML string injection is involved on this path — every element reaches the DOM through React reconciliation, so the output is amenable to ordinary React composition (selectable text spans, hover affordances, snapshot tests). The walker mirrors the DOM contract `chordsketch-render-html` produces (`.song`, `.line`, `.chord-block`, `
`, `

`, etc.) so the bundled `@chordsketch/react/styles.css` lights up unchanged. See [ADR-0017](https://github.com/koedame/chordsketch/blob/main/docs/adr/0017-react-renders-from-ast.md) for the architectural split. `format="text"` renders the plain-text chords-above-lyrics output inside a `

`; pick that variant if you need an
even-more-conservative preview that avoids the JSX walker
entirely.

**Trust boundary note.** The walker enforces the same URI-scheme
blocklist (`javascript:`, `vbscript:`, `data:`, `file:`,
`blob:`) `chordsketch-render-html` applies, so image directives
with dangerous schemes drop out of the output the same way they
do on the static-HTML side. Delegate sections (`{start_of_abc}`,
`{start_of_ly}`, `{start_of_musicxml}`, `{start_of_textblock}`)
are tracked as a follow-up — the walker currently ignores their
bodies rather than rendering them. If you accept untrusted
ChordPro and need full delegate-section rendering today, drive
the static `chordsketch-render-html` output yourself and embed
it in your own iframe.

Errors are surfaced via an inline `role="alert"` above the
render by default. Pass `errorFallback={(err) => }` to
customise — any ReactNode works under both `format` values
because the error lives in a sibling element of the rendered
output. `errorFallback={null}` hides errors entirely and lets
the stale previous render stay visible.

### `useChordRender` — hook for bespoke renderers

```tsx
import { useChordRender } from '@chordsketch/react';

const { output, loading, error } = useChordRender(source, 'html', {
  transpose: 2,
});
```

Same render pipeline as `` but exposed as raw state —
wire the output into a custom container (e.g. a diff view, a
multi-pane preview). The renderer is memoised against
`(source, format, transpose, config)`, so re-renders with
unchanged inputs do not re-parse.

### `` — split-pane textarea + live preview (Tier 1 atom)

```tsx
import { ChordTextarea, useTranspose } from '@chordsketch/react';

export function Editor() {
  const { value: transpose, setValue: setTranspose } = useTranspose();
  return (
    
  );
}
```

The left pane is a plain `