npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vexflow-native

v1.0.1

Published

React native backend for rendering music notation using @shopify/react-native-skiaact-native-skia

Readme

vexflow-native

React Native Skia bridge for rendering VexFlow music notation.

Installation

Install the library and its peer dependencies:

npm install vexflow-native react react-native vexflow @shopify/react-native-skia react-native-gesture-handler react-native-reanimated react-native-worklets

ScoreRenderer uses gesture handling and Reanimated worklets for scrolling, so configure react-native-gesture-handler, react-native-reanimated, and react-native-worklets as required by your React Native app.

Entrypoints

  • vexflow-native exports the low-level VexflowCanvas bridge for drawing with VexFlow directly.
  • vexflow-native/renderer exports ScoreRenderer, a React component that renders the typed score model.
  • vexflow-native/state exports the score state types, including Score.
  • vexflow-native/musicxml exports parseMusicXmlToScore and MusicXmlParseError.

Fonts

Load a notation font with Skia and pass the returned font provider to the renderer. The defaultFont prop must match one of the font family names passed to useFonts.

import { useFonts } from '@shopify/react-native-skia';

import bravuraFont from './assets/fonts/Bravura.otf';

const fontManager = useFonts({
  Bravura: [bravuraFont],
});

if (!fontManager) {
  return null;
}

React Native Skia web patches

The example app includes a patch-package patch for @shopify/[email protected] at example/patches/@shopify+react-native-skia+2.6.2.patch. The repository root applies it after install with:

yarn workspace vexflow-native-example apply-patches

The patch is needed by the web example with this Skia version. It:

  • lets Skia web resolve string asset sources;
  • forwards JsiSkTypefaceFontProvider.matchFamilyStyle to CanvasKit so fonts registered with useFonts can be matched by family name;
  • keeps a small diagnostic log in the web font manager patch.

Native iOS and Android usage does not need this example patch. If your own app targets React Native Web with @shopify/[email protected] and hits font matching or asset source issues, apply an equivalent patch in your app with patch-package. Re-check the patch when upgrading Skia, because it is tied to that package version and may become unnecessary after upstream changes.

Render MusicXML

Convert a MusicXML string into a Score, then render it with ScoreRenderer.

import React, { useMemo } from 'react';
import { View } from 'react-native';
import { useFonts } from '@shopify/react-native-skia';
import { ScoreRenderer } from 'vexflow-native/renderer';
import {
  MusicXmlParseError,
  parseMusicXmlToScore,
} from 'vexflow-native/musicxml';

import bravuraFont from './assets/fonts/Bravura.otf';

type MusicXmlScoreProps = {
  xml: string;
};

export function MusicXmlScore({ xml }: MusicXmlScoreProps) {
  const fontManager = useFonts({
    Bravura: [bravuraFont],
  });
  const score = useMemo(
    () => parseMusicXmlToScore(xml, { scoreId: 'imported-score' }),
    [xml]
  );

  if (!fontManager) {
    return null;
  }

  return (
    <View style={{ flex: 1 }}>
      <ScoreRenderer
        score={score}
        defaultFont="Bravura"
        fontManager={fontManager}
      />
    </View>
  );
}

try {
  parseMusicXmlToScore('<score-timewise />');
} catch (error) {
  if (error instanceof MusicXmlParseError) {
    // The current parser targets score-partwise MusicXML.
  }
}

The MusicXML parser currently targets score-partwise documents. score-timewise input throws MusicXmlParseError.

Render a Plain Score Object

Use ScoreRenderer directly when you already have score state or want to build it yourself.

import React from 'react';
import { View } from 'react-native';
import { useFonts } from '@shopify/react-native-skia';
import { ScoreRenderer } from 'vexflow-native/renderer';
import type { Score } from 'vexflow-native/state';

import bravuraFont from './assets/fonts/Bravura.otf';

const score: Score = {
  id: 'plain-score',
  defaults: {
    meter: {
      beats: 4,
      beatUnit: 4,
    },
    keySignature: {
      tonic: 'C',
      mode: 'major',
    },
  },
  staves: [
    {
      id: 'staff-1',
      order: 0,
      defaultClef: 'treble',
      measures: [
        {
          id: 'measure-1',
          number: 1,
          leftModifiers: {
            showClef: true,
            showKeySignature: true,
            showMeter: true,
          },
          voices: [
            {
              id: 'voice-1',
              index: 0,
              items: [
                {
                  id: 'note-1',
                  type: 'note',
                  pitch: { step: 'C', octave: 4 },
                  duration: { length: 'q' },
                  voiceId: 'voice-1',
                },
                {
                  id: 'note-2',
                  type: 'note',
                  pitch: { step: 'D', octave: 4 },
                  duration: { length: 'q' },
                  voiceId: 'voice-1',
                },
                {
                  id: 'note-3',
                  type: 'note',
                  pitch: { step: 'E', octave: 4 },
                  duration: { length: 'q' },
                  voiceId: 'voice-1',
                },
                {
                  id: 'note-4',
                  type: 'note',
                  pitch: { step: 'F', octave: 4 },
                  duration: { length: 'q' },
                  voiceId: 'voice-1',
                },
              ],
            },
          ],
        },
      ],
    },
  ],
};

export function PlainScore() {
  const fontManager = useFonts({
    Bravura: [bravuraFont],
  });

  if (!fontManager) {
    return null;
  }

  return (
    <View style={{ flex: 1 }}>
      <ScoreRenderer
        score={score}
        defaultFont="Bravura"
        fontManager={fontManager}
        rendererType="document"
      />
    </View>
  );
}

rendererType is optional. The default is document; use documentEven for even measure widths across document systems, or infiniteScore for a horizontal single-system layout.

Draw Directly with VexFlow

Use VexflowCanvas when you want direct VexFlow control instead of the typed score model.

import React, { useCallback } from 'react';
import { View } from 'react-native';
import { useFonts } from '@shopify/react-native-skia';
import { Formatter, Stave, StaveNote, Voice } from 'vexflow';
import { VexflowCanvas, type OnDrawParams } from 'vexflow-native';

import bravuraFont from './assets/fonts/Bravura.otf';

export function DirectVexFlow() {
  const fontManager = useFonts({
    Bravura: [bravuraFont],
  });

  const onDraw = useCallback(({ ctx }: OnDrawParams) => {
    const stave = new Stave(10, 40, 400);

    stave.addClef('treble').addTimeSignature('4/4');

    const notes = [
      new StaveNote({ keys: ['c/4'], duration: 'q' }),
      new StaveNote({ keys: ['d/4'], duration: 'q' }),
      new StaveNote({ keys: ['e/4'], duration: 'q' }),
      new StaveNote({ keys: ['f/4'], duration: 'q' }),
    ];

    const voice = new Voice({ numBeats: 4, beatValue: 4 });
    voice.addTickables(notes);

    new Formatter().joinVoices([voice]).formatToStave([voice], stave);

    stave.setContext(ctx).draw();
    voice.draw(ctx, stave);
  }, []);

  if (!fontManager) {
    return null;
  }

  return (
    <View style={{ flex: 1 }}>
      <VexflowCanvas
        onDraw={onDraw}
        fontManager={fontManager}
        defaultFont="Bravura"
      />
    </View>
  );
}

ScoreRenderer props

  • score: typed score state to render.
  • defaultFont: font family name used as the default VexFlow font.
  • fontManager: Skia font provider returned by useFonts.
  • colorScheme: optional foreground, background, and ledger line colors.
  • rendererType: document, documentEven, or infiniteScore.
  • options: optional renderer settings grouped by insets, spacing, and render.
  • scrollEnabled: enables or disables pan scrolling. Defaults to true.
  • showScrollbars: shows scrollbars when the score overflows. Defaults to true.

Contributing

License

MIT