Embed ChordPro and iReal Pro in your React app
@chordsketch/react ships the same parser + renderer pipeline that
powers https://chordsketch.koeda.me as a published React component
library. This page is the recipe collection for the most common
embedding scenarios; copy-paste into a fresh Vite + React app
(or Next.js, see §Server-side rendering / Next.js
below) and it works.
Prerequisite.
npm install @chordsketch/react react react-dom. The PDF / PNG export bundle is a separate optional peer, imported from a separate@chordsketch/react/pdfsubpath — see §Export to PDF for when to install it. This applies to Next.js too; unlike the main entry point, only apps that import@chordsketch/react/pdfneed the peer.
Recipe 1 — Drop in a ChordPro playground in 30 seconds
The fastest path. One component, no configuration, full editor + preview + transpose UI:
import { ChordProEditor } from '@chordsketch/react';
import '@chordsketch/react/styles.css';
export default function App() {
return <ChordProEditor defaultSource={"{title: My Song}\n[G]Hello [D]world"} />;
}<ChordProEditor> accepts source + onSourceChange to drive the
value from the host (controlled mode) instead of letting the component
own it.
Renamed from
<Playground>in@chordsketch/reactv0.3.0 per ADR-0022.
Recipe 2 — Render a read-only chord sheet
For lyrics-and-chords display without any editing affordance:
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 <ChordSheet source={source} transpose={0} />;
}format="html" (the default) walks the parsed AST into a React tree
through the chordpro-jsx walker — every element reaches the DOM
through React reconciliation, so the output is safe for snapshot
tests and ordinary React composition. format="text" switches to
a <pre>-wrapped plain-text render for an even-more-conservative
preview.
Recipe 3 — Build a custom editor layout
For hosts that want their own pane layout, <ChordSourceArea> (the
CodeMirror 6 editor with ChordPro syntax highlight) and
<RendererPreview> (the format-switching preview pane) compose
freely:
import { useState } from 'react';
import { ChordSourceArea, RendererPreview, SplitLayout } from '@chordsketch/react';
import '@chordsketch/react/styles.css';
export function Editor() {
const [source, setSource] = useState('{title: My Song}\n[G]Hello');
return (
<SplitLayout
start={<ChordSourceArea value={source} onChange={setSource} />}
end={<RendererPreview source={source} format="html" />}
/>
);
}
<ChordSourceArea>was renamed from<SourceEditor>in@chordsketch/reactv0.3.0.
<SplitLayout> exposes a --cs-split-ratio CSS variable for the
two-pane ratio and falls back to a stacked layout under 768 px.
Recipe 4 — Add transposition controls
<Transpose> is a native <select> listing every semitone offset
between min and max — keyboard and screen-reader support come
from the browser's own control. Pair it with the transpose prop on
<ChordSheet>:
import { ChordSheet, Transpose, useTranspose } from '@chordsketch/react';
import '@chordsketch/react/styles.css';
const source = `{title: Hello}\n[Am]hello [F]world`;
export function Sheet() {
const { value, setValue } = useTranspose();
return (
<div>
<Transpose value={value} onChange={setValue} />
<ChordSheet source={source} transpose={value} />
</div>
);
}The two defaults differ on purpose: useTranspose() clamps to the
feature limit ±11 (a full octave is the identity, so ±12 renders
the written chords), while the select offers the narrower ±6 that
is useful in practice. Pass min / max to <Transpose> to widen
the option list to whatever range the hook is clamping to.
useTranspose() also returns increment / decrement / reset
for hosts that build their own control (slider, number input,
keyboard shortcut). Every update clamps, and reset() returns to
the initial value — not necessarily zero.
Recipe 5 — Export to PDF
PDF export ships in a separate heavy bundle so the lean
@chordsketch/wasm core stays small, behind the @chordsketch/react/pdf
subpath rather than the package root. Install the optional peer
alongside @chordsketch/react:
npm install @chordsketch/wasm-exportThen drop in <PdfExport>:
import { PdfExport } from '@chordsketch/react/pdf';
const source = `{title: Amazing Grace}
{key: G}
[G]Amazing [G7]grace, how [C]sweet the [G]sound`;
export function SaveButton() {
return (
<PdfExport source={source} filename="amazing-grace.pdf">
Export PDF
</PdfExport>
);
}The heavy bundle is lazy-loaded on first export — the initial
page load does not pay for it. <PdfExport> exposes the standard
<button> attributes (className, style, type, …) plus
onExported(filename) and onError(err) callbacks for analytics
or toasts.
usePdfExport() returns the same exportPdf pipeline as state for
custom UIs (dropdown items, command palettes, etc.).
<RendererPreview format="pdf">, <PreviewToolbar>, <ChordProPreview>,
and <ChordProEditor> do not import @chordsketch/react/pdf on
their own — pass <PdfExport> through their pdfExportComponent
prop to enable PDF export in those surfaces:
import { ChordProEditor } from '@chordsketch/react';
import { PdfExport } from '@chordsketch/react/pdf';
export function EditorWithPdf() {
return <ChordProEditor pdfExportComponent={PdfExport} defaultSource="{title: Hello}" />;
}This split keeps @chordsketch/react's main entry point free of the
import('@chordsketch/wasm-export') call — bundlers that resolve
dynamic imports at build time (webpack, Turbopack, and so Next.js —
see Recipe 10) never
require the peer for apps that do not import @chordsketch/react/pdf.
Recipe 6 — Render chord diagrams
<ChordDiagram> looks up the chord in the built-in voicing
database (156 voicings: 60 guitar, 36 ukulele, 60 piano) and
returns inline SVG that inherits currentColor:
import { ChordDiagram } from '@chordsketch/react';
import '@chordsketch/react/styles.css';
export function Voicings() {
return (
<>
<ChordDiagram chord="Am" instrument="guitar" />
<ChordDiagram chord="C" instrument="ukulele" />
<ChordDiagram chord="Dm7" instrument="piano" />
</>
);
}Unknown chords render notFoundFallback (default: an inline
role="note" with the chord name). useChordDiagram() returns the
raw SVG string for hosts that want to embed it inside custom markup
(tooltip, popover, etc.).
Recipe 7 — Drive your own UI from the ChordPro AST
For hosts that want to render the song in a completely custom way
— a karaoke prompter, an alternate layout for printing, a
syntax-highlighted source view — use useChordproAst and
renderChordproAst:
import { useChordproAst, renderChordproAst } from '@chordsketch/react';
export function CustomRender({ source }: { source: string }) {
const { ast, warnings, loading, error } = useChordproAst(source);
if (loading) return <p>Loading…</p>;
if (error) return <p role="alert">{error.message}</p>;
if (ast === null) return null;
return (
<article>
<div>{renderChordproAst(ast)}</div>
{warnings.length > 0 ? (
<details>
<summary>Warnings ({warnings.length})</summary>
<ul>
{warnings.map((w, i) => (
<li key={i}>{w}</li>
))}
</ul>
</details>
) : null}
</article>
);
}renderChordproAst is also the function <ChordSheet format="html">
uses internally — driving it directly gives identical output you
can place anywhere in your tree, song title included. Read
ast.metadata (title, artists, key, …) when the host wants
those fields for its own chrome — a page <title>, a setlist row —
rather than for the sheet itself. warnings is a list of
human-readable strings.
Recipe 8 — Drop in an iReal Pro playground
<IrealProEditor> is the iReal Pro sibling of <ChordProEditor> — a
single-component embed for an iReal Pro chart:
import { IrealProEditor } from '@chordsketch/react';
import '@chordsketch/react/styles.css';
const URL =
'irealb://Autumn%20Leaves%3DKosma%20Joseph%3D%3DMedium%20Swing%3DG%2D%3D%3D' +
'1r34LbKcu7T44%2AA%5BC%2D7%7CF7%7CBb%5E7%7CEb%5E7%7CAh7%7CD7%7CG%2D6%7CG%2D6Z' +
'%3D%3D0%3D0';
export default function App() {
return <IrealProEditor defaultValue={URL} />;
}Renamed from
<IrealPlayground>in@chordsketch/reactv0.3.0 per ADR-0022.
The composite shows the editor (header form + interactive bar grid
with structural editing + URL textarea) next to the SVG preview.
hidePreview, hideBars, and hideUrl trim the layout for
narrower hosts; pass source + onChange for controlled mode.
Editing scope.
v0.2.0brings the iReal Pro surface to parity with the private@chordsketch/ui-irealb-editorper ADR-0020: structural section / bar editing, ARIA-grid keyboard navigation, and a popover-based per-bar chord editor with a chord-row editor, N-th ending input, and symbol picker. The playground at https://chordsketch.koeda.me/chordsketch/irealpro/ still hosts the DOM editor for reference comparison.
Recipe 9 — Custom iReal Pro rendering
For hosts that need their own iReal Pro UI, the <IrealPreview>
component renders the SVG directly:
import { IrealPreview, useIrealParse } from '@chordsketch/react';
export function CustomChart({ url }: { url: string }) {
const { song, error } = useIrealParse(url);
return (
<article>
{song !== null ? <h1>{song.title}</h1> : null}
<IrealPreview source={url} />
{error ? <p role="alert">{error.message}</p> : null}
</article>
);
}useIrealParse exposes the typed IrealSong AST so the host can
build any UI on top: a setlist filtered by key_signature.mode, a
search box that matches against composer, a key-changer that
edits song.transpose and re-serialises via useIrealSerialize.
Recipe 10 — Server-side rendering / Next.js
npm install @chordsketch/reactNext.js's bundler (webpack / Turbopack) resolves every import() at
build time, including dynamic imports inside packages it never
executes on the server. @chordsketch/react's main entry point does
not reference @chordsketch/wasm-export at all — that lazy import
lives behind the separate @chordsketch/react/pdf subpath (see
§Export to PDF) — so next build succeeds
without installing the heavy peer unless your app actually imports
@chordsketch/react/pdf. If it does, install
@chordsketch/wasm-export alongside it; the bundle is still only
fetched on the first export, so installing it does not grow the page.
The components hold state and touch window / document on mount,
so they are Client Components, and @chordsketch/react does not mark
its entry point with 'use client'. Re-export what you need from a
file that does:
// app/song/[id]/sheet.tsx
'use client';
import { ChordSheet } from '@chordsketch/react';
export { ChordSheet };Then import from that file — not from @chordsketch/react — in the
Server Component. Importing the library directly into a Server
Component fails at request time with
useState is not a function or its return value is not iterable.
// app/song/[id]/page.tsx
import { ChordSheet } from './sheet';
import '@chordsketch/react/styles.css';
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
// The server renders the component's loading state; the
// wasm-backed render runs on the client, where the browser
// caches the binary.
return <ChordSheet source={`{title: ${id}}\n[G]Hello [D]world`} />;
}params is a Promise from Next.js 15 on; on Next.js 14, type it as
{ params: { id: string } } and read params.id directly.
In practice, prefer rendering the preview on the client even for
static content — the browser's HTTP cache stores
chordsketch_wasm_bg.wasm once and reuses it across navigations,
which the Node require cache cannot do across deployments.
For pure SSR (e.g. generating an OG image, emailing a PDF), drive the
wasm packages directly from a Node module — render_html_with_options
from @chordsketch/wasm, render_pdf from @chordsketch/wasm-export
— and call them synchronously. The React components are the wrong
layer for non-React server rendering.
See also
- Embed ChordPro in a Vue app — the same recipes in
the same order for
@chordsketch/vue, and the list of surfaces on this page that have no Vue counterpart. - Embed ChordPro in a Svelte app — the same
recipes again for
@chordsketch/svelte, and the list of surfaces on this page that have no Svelte counterpart. - Render to HTML, plain text, or PDF — same operation across every binding (CLI / Rust / Python / Swift / Kotlin / Ruby / wasm), useful if your stack mixes a React client with a non-React server.
- Transpose chords by N semitones — the transposition surface across bindings, for hosts that want to pre-compute transpositions outside React.
packages/react/README.md— the full API reference for@chordsketch/react, including the AST type re-exports and helper functions.- ADR-0020 — why the
iReal Pro React surface is a native React implementation rather
than a wrapper around the private
@chordsketch/ui-irealb-editor.