@viritura/score-viewer-react
v0.1.0
Published
Embeddable React score viewer components for rendering MNX music notation in the browser. Wraps @viritura/score-engine.
Maintainers
Readme
@viritura/score-viewer-react
Embeddable React components (
<ScoreViewer>and<ScoreView>) for rendering MNX music notation in the browser. Thin wrappers over@viritura/score-viewer.
Status:
0.x. Breaking changes are possible before1.0.0; pin an exact version. Requires React 19.2 or later.
Quick start
npm install @viritura/score-viewer-reactCopy the engine's runtime files into your static assets (see the
@viritura/score-engine README),
then:
import { ScoreViewer } from "@viritura/score-viewer-react";
function MyDocs() {
return (
<ScoreViewer
mnx={mnxJsonString}
assetBaseUrl="/score-engine/"
defaultFitMode="width"
defaultViewMode="page"
enableCtrlWheelZoom
/>
);
}That's it. The DOM viewer lazy-loads the WASM engine + Bravura font on first mount; subsequent instances share the same engine.
Use <ScoreViewer> when you want the complete embeddable viewer with zoom,
fit, view-mode controls, and Ctrl-scroll zoom. Use <ScoreView> when you want
only the score canvases and plan to provide your own chrome.
For cropped fragments embedded in tight panels, pass bare to remove the
viewer's default viewport padding (the Drum Kit editor uses this mode).
View Modes
<ScoreViewer
mnx={mnxJsonString}
availableViewModes={["page", "horizontal", "spread", "spread-horizontal", "horizon"]}
defaultViewMode="spread"
defaultFitMode="width"
controls={{ score: true, viewMode: true, zoom: true, fit: true }}
/>horizon uses the same continuous, unpaginated layout as the Viritura editor
and renders only the visible tiles so very long scores remain browser-safe.
Hosts can expose page and staff-size selectors with pageSizeOptions,
staffSizeOptions, and the corresponding controls flags. These selectors are
hidden automatically in Horizon because it has no physical pages.
If an MNX document contains multiple scores[] entries, hosts can provide
scoreOptions and handle onScoreIndexChange to show a score selector in the
same control surface.
For hosts that need rewritten asset URLs, such as VS Code webviews, pass a base
URL containing wasm/ and fonts/ folders:
<ScoreViewer mnx={mnxJsonString} assetBaseUrl={webviewAssetBaseUrl} />With a playhead
import { useState, useEffect } from "react";
import { ScoreView } from "@viritura/score-viewer-react";
function PlayingScore({ mnx }: { mnx: string }) {
const [beat, setBeat] = useState(0);
useEffect(() => {
const id = setInterval(() => setBeat((b) => b + 0.5), 250);
return () => clearInterval(id);
}, []);
return (
<ScoreView mnx={mnx} pageWidth={800}>
<ScoreView.Playhead beat={beat} partId="p1" follow style={{ color: "red" }} />
</ScoreView>
);
}Per-page overlays
<ScoreView mnx={mnx} pageWidth={800} pagesPerRow={2}>
<ScoreView.Page page={0}>
<div style={{ position: "absolute", top: 8, right: 8, color: "#888" }}>Page 1</div>
</ScoreView.Page>
<ScoreView.Page page={1}>
<div style={{ position: "absolute", top: 8, right: 8, color: "#888" }}>Page 2</div>
</ScoreView.Page>
</ScoreView>Hooks
For advanced consumers that need the engine + display list directly:
import { useScoreEngine } from "@viritura/score-viewer-react";
function CustomViewer({ mnx }: { mnx: string }) {
const { engine, displayList, loading, error } = useScoreEngine(mnx, {
pageWidth: 800,
});
if (loading) return <div>Loading…</div>;
if (error) return <div>Error: {error.message}</div>;
if (!engine || !displayList) return null;
// Roll your own paint loop, custom overlays, hit-testing, etc.
return <CustomCanvas engine={engine} displayList={displayList} />;
}Props
| Prop | Type | Default | Notes |
| ----------------- | ------------------------------------------------------------- | -------- | ---------------------------------------------------------------- |
| mnx | string \| object | — | MNX JSON (string or parsed) |
| pageWidth | number | 800 | Page width; ignored in horizon |
| pageHeight | number | A4 ratio | Page height in display-list units |
| pageMargins | { top, right, bottom, left } | 15 mm | Margins scaled to the default A4 width |
| spatium | number | 7 | Staff-space height in display-list units |
| scoreIndex | number | 0 | Which scores[] entry to render |
| scoreOptions | { index: number; label: string }[] | [] | Options for the score selector |
| zoom | number \| "fit-width" \| "fit-page" | 1 | 1.0 = 1 display-list pixel = 1 CSS px |
| viewMode | ScoreViewMode | "page" | page, horizontal, spread, spread-horizontal or horizon |
| gap | number | 16 | CSS px between pages |
| assetBaseUrl | string | — | Base URL of the engine's wasm/, fonts/ and worker |
| pagesPerRow | number | 1 | Multi-page layout |
| onReady | (info: { engine, displayList }) => void | — | |
| onError | (err: EngineLoadError \| ParseError \| LayoutError) => void | — | |
| loadingFallback | ReactNode | text | Custom loading UI |
| errorFallback | (err: Error) => ReactNode | text | Custom error UI |
| className | string | — | |
| style | CSSProperties | — | |
viewMode="horizontal" arranges rendered pages in one row; it does not select
the engine's unpaged layout. Use viewMode="horizon" for one continuous,
unpaged system.
Public documentation: viritura.com/developers/score-viewer-react.
Composition slots
<ScoreView.Page page={n}>— overlay container positioned over pagen. Useful for badges, comments, annotations.<ScoreView.Playhead beat={n} partId="p1">— vertical playhead line auto-positioned viaengine.playhead. Passfollowto keep it visible while playback advances, orrender={fn}to customize the visual.
License
MIT
