Skip to content

Greg Jopa

Integrating Vexflow with Gatsby.js

Gatsby.js, Vexflow

My new blog is built with Gatsby.js and my goal is to make easy to insert sheet music and guitar tab into blog posts. I'm a big fan of the Vexflow Music Notation library and for this post I'm going to explain how I got Vexflow working with Gatsby.

Demo

I struggled a bit with server-side rendering, but eventually got it working! Here's the end result:

Loading...

Integration Details

There are 3 things I had to do to get Vexflow working with Gatsby.js.

  1. react-loadable - The Gatsby framework renders everything server-side and Vexflow does not play nicely by default. The Vexflow library depends on 'window' and several other browser features. To work around this issue I used react-loadable to only load my VexflowDemo component on the client-side.

    1import React from "react"
    2import Loadable from "react-loadable"
    3
    4export default Loadable({
    5 // this "./VexflowDemo" component loads Vexflow and draws the sheet music
    6 loader: () => import("./VexflowDemo"),
    7 loading() {
    8 return <div>Loading...</div>
    9 }
    10})
  2. DOM Node Ref - the Vexflow library takes care of the complex rendering for creating sheet music. It just needs a specific container element to use. The 'ref' feature makes it easy to share the container element with Vexflow without having to hardcode any id values.

  3. useEffect Hook - the Vexflow rendering process requires a DOM node reference so we need to make sure the DOM is ready. With React, we can accomplish this using 'useEffect' or 'componentDidMount()'.

Here's a snippet showing how to use 'useEffect' with 'ref':

1function VexflowDemo(props) {
2 const notesContainerRef = React.useRef(null)
3
4 useEffect(() => {
5 if (notesContainerRef.current !== null) {
6 // this draw() function contains all Vexflow code for rendering the sheet music
7 draw(notesContainerRef.current, props)
8 }
9 })
10
11 return (
12 <div ref={notesContainerRef} />
13 )
14}

Check out the full source code for this blog post.